From d7ea0b99acc8a96f8fc2a2a71d4a3c0392be276a Mon Sep 17 00:00:00 2001 From: Nikola Petrov Date: Tue, 3 Feb 2026 15:37:49 +0100 Subject: [PATCH] Add similarity and experimental drawBranch --- CMakeLists.txt | 56 ++++----- app/src/DnaStore.cpp | 2 +- shared/inc/canvas/Tree.hpp | 3 +- shared/inc/values/Similarity.hpp | 10 +- shared/src/canvas/BackGround.cpp | 1 + shared/src/canvas/Tree.cpp | 62 ++++++++-- shared/src/values/Similarity.cpp | 189 ++++++++++++++++++++++++------- view/inc/Vapp.hpp | 4 +- view/src/Vapp.cpp | 82 ++++++++++++-- 9 files changed, 311 insertions(+), 98 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b96e61..aa6c4fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,11 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) # set(CMAKE_VERBOSE_MAKEFILE ON) add_compile_options(-ggdb) -include_directories(external/include/) +include_directories( + external/include + shared/inc +) + link_libraries( ${CMAKE_SOURCE_DIR}/external/libimgui.a ${CMAKE_SOURCE_DIR}/external/libraylib.a @@ -18,50 +22,38 @@ link_libraries( m ) +add_library(shared STATIC + shared/src/canvas/BackGround.cpp + shared/src/canvas/BackGroundColors.cpp + shared/src/canvas/Canvas.cpp + shared/src/canvas/Tree.cpp + shared/src/values/Dna.cpp + shared/src/values/DnaManager.cpp + shared/src/values/mrand.cpp + shared/src/values/Similarity.cpp + shared/src/TcpSocket.cpp + shared/src/sql.cpp +) + add_executable(app app/src/main.cpp app/src/App.cpp app/src/DnaStore.cpp app/src/sys.cpp - shared/src/canvas/BackGround.cpp - shared/src/canvas/BackGroundColors.cpp - shared/src/canvas/Canvas.cpp - shared/src/canvas/Tree.cpp - shared/src/values/Dna.cpp - shared/src/values/DnaManager.cpp - shared/src/values/mrand.cpp - shared/src/values/Similarity.cpp - shared/src/TcpSocket.cpp ) -# Add include directories -target_include_directories(app PRIVATE app/inc shared/inc ) +target_include_directories(app PRIVATE app/inc) +target_link_libraries(app PRIVATE shared) add_executable(server server/src/server.cpp server/src/checker.cpp - - shared/src/sql.cpp - shared/src/TcpSocket.cpp - shared/src/values/Dna.cpp - shared/src/values/DnaManager.cpp - shared/src/values/mrand.cpp ) -# Add include directories -target_include_directories(server PRIVATE server/inc shared/inc) +target_include_directories(server PRIVATE server/inc) +target_link_libraries(server PRIVATE shared) add_executable(view view/src/main.cpp view/src/Vapp.cpp - shared/src/canvas/BackGround.cpp - shared/src/canvas/BackGroundColors.cpp - shared/src/canvas/Canvas.cpp - shared/src/canvas/Tree.cpp - shared/src/values/Dna.cpp - shared/src/values/DnaManager.cpp - shared/src/values/mrand.cpp - shared/src/values/Similarity.cpp - - shared/src/sql.cpp ) -# Add include directories -target_include_directories(view PRIVATE view/inc shared/inc) \ No newline at end of file +target_include_directories(view PRIVATE view/inc) +target_link_libraries(view PRIVATE shared) diff --git a/app/src/DnaStore.cpp b/app/src/DnaStore.cpp index d0f0c45..7c96a7e 100644 --- a/app/src/DnaStore.cpp +++ b/app/src/DnaStore.cpp @@ -16,7 +16,7 @@ #define DATA_FILE_NAME "DATA.bin" #define VECTOR_FILE_NAME "VECTOR.bin" #define GEN_FILE_PATTRN "gen/%04d.bin" -#define HOST_NAME "petrovv.com" +#define HOST_NAME "localhost" void DnaStore::load(DnaManagerData *data) { diff --git a/shared/inc/canvas/Tree.hpp b/shared/inc/canvas/Tree.hpp index 762185a..e4ab0ac 100644 --- a/shared/inc/canvas/Tree.hpp +++ b/shared/inc/canvas/Tree.hpp @@ -32,8 +32,9 @@ private: Vector2 start = {0}; std::list drawCalls; - void drawBranch(); + void calculateBranch(); + void drawBranch(Vector2 startPoint, Vector2 endPoint, Color startColor, Color endColor, float startThickness, float endThickness); inline size_t getNumOfBranches(int dep); inline Color getStartColor(DrawArgs &arg); inline Color getEndColor(int dep, Color &start); diff --git a/shared/inc/values/Similarity.hpp b/shared/inc/values/Similarity.hpp index ed01179..29c819e 100644 --- a/shared/inc/values/Similarity.hpp +++ b/shared/inc/values/Similarity.hpp @@ -3,14 +3,14 @@ namespace Similarity { - // float euclidean_distance(Dna *d1, Dna *d2); direct distance betwen vector. wont give 0 and 1 - // float dot_product(Dna *d1, Dna *d2); doent return betwen 0 to 1 - // float cosine_similarity(Dna *d1, Dna *d2); - // float cosine_similarity_int(Dna *d1, Dna *d2); + float euclidean_distance(Dna *d1, Dna *d2);// direct distance betwen vector. wont give 0 and 1 + float dot_minmax(Dna *d1, Dna *d2); // doent return betwen 0 to 1 + float cosine_similarity(Dna *d1, Dna *d2); + float cosine_similarity_int(Dna *d1, Dna *d2); float hamming_distance(Dna *d1, Dna *d2); float hamming_distance_without_seeds(Dna *d1, Dna *d2); // float jaccard_index(Dna *d1, Dna *d2); // primerja unio genov naprimer gleda ce je gen za nebo isti z genom za barvo za liste, to nerabimo - // float levenshtein_distance(Dna *d1, Dna *d2); // odstranjen ker mi vrne iste podatke kot hamming distance ki je bolj enostaven za izracun + float levenshtein_distance(Dna *d1, Dna *d2); // odstranjen ker mi vrne iste podatke kot hamming distance ki je bolj enostaven za izracun // float needleman_wunsch(Dna *d1, Dna *d2); used for bioinformatics and aligment. Dont need its aligned alredy typedef float(simil_func)(Dna *d1, Dna *d2); diff --git a/shared/src/canvas/BackGround.cpp b/shared/src/canvas/BackGround.cpp index 4743398..7353db0 100644 --- a/shared/src/canvas/BackGround.cpp +++ b/shared/src/canvas/BackGround.cpp @@ -1,4 +1,5 @@ #include +#include #include "canvas/BackGround.hpp" #include "canvas/BackGroundColors.hpp" diff --git a/shared/src/canvas/Tree.cpp b/shared/src/canvas/Tree.cpp index 35e95cb..3ffb08d 100644 --- a/shared/src/canvas/Tree.cpp +++ b/shared/src/canvas/Tree.cpp @@ -4,6 +4,7 @@ #include #include +#include #define ITER_PER_FRAME 5000 @@ -40,7 +41,7 @@ void Tree::init(int size) start.x = size / 2; start.y = size; calculateLevels(size); - //texBunny = LoadTexture("dot.png"); // bug add deinit to unload texutre + // texBunny = LoadTexture("dot.png"); // bug add deinit to unload texutre } void Tree::draw(Dna *dna) @@ -56,7 +57,7 @@ bool Tree::tick() size_t i = 0; while (!drawCalls.empty()) { - drawBranch(); + calculateBranch(); drawCalls.pop_front(); i++; if (i >= ITER_PER_FRAME) @@ -68,7 +69,7 @@ bool Tree::tick() // Private -void Tree::drawBranch() +void Tree::calculateBranch() { DrawArgs arg = drawCalls.front(); if (arg.dep == MAX_DEPTH) @@ -86,17 +87,14 @@ void Tree::drawBranch() Color colorStart = getStartColor(arg); Color colorEnd = getEndColor(arg.dep, colorStart); - + // drawBranch(arg.start, end, colorStart, colorEnd, sizeStart, sizeEnd); for (float i = 0; i < 1; i += fstep) { Vector2 point = Vector2Lerp(arg.start, end, i); Color color = ColorLerp(colorStart, colorEnd, i); int size = Lerp(sizeStart, sizeEnd, i); DrawCircleV(point, size, color); - //DrawTextureEx(texBunny, point,0, ((float)size) / texBunny.height, color); - - // use - // DrawRectangleGradientEx + // DrawTextureEx(texBunny, point,0, ((float)size) / texBunny.height, color); } // add more branches to draw @@ -200,3 +198,51 @@ inline float Tree::getAngleVar(DrawArgs &arg) return angleVar; } + +void Tree::drawBranch(Vector2 startPoint, Vector2 endPoint, Color startColor, Color endColor, float startThickness, float endThickness) +{ + // Calculate the direction vector from startPoint to endPoint + Vector2 direction = {endPoint.x - startPoint.x, endPoint.y - startPoint.y}; + + // Normalize the direction vector + float length = sqrtf(direction.x * direction.x + direction.y * direction.y); + if (length == 0) + length = 1; // Avoid division by zero + Vector2 normalizedDir = {direction.x / length, direction.y / length}; + + // Calculate the perpendicular vector (rotate 90 degrees) + Vector2 perpendicular = {-normalizedDir.y, normalizedDir.x}; + + // Calculate the four vertices of the quadrilateral + Vector2 topLeft = { + startPoint.x + perpendicular.x * startThickness, + startPoint.y + perpendicular.y * startThickness}; + Vector2 topRight = { + endPoint.x + perpendicular.x * endThickness, + endPoint.y + perpendicular.y * endThickness}; + Vector2 bottomLeft = { + startPoint.x - perpendicular.x * startThickness, + startPoint.y - perpendicular.y * startThickness}; + Vector2 bottomRight = { + endPoint.x - perpendicular.x * endThickness, + endPoint.y - perpendicular.y * endThickness}; + + // Draw the two triangles to form the quadrilateral + rlBegin(RL_TRIANGLES); + // First triangle + rlColor4ub(startColor.r, startColor.g, startColor.b, startColor.a); + rlVertex2f(topLeft.x, topLeft.y); + rlColor4ub(endColor.r, endColor.g, endColor.b, endColor.a); + rlVertex2f(topRight.x, topRight.y); + rlColor4ub(startColor.r, startColor.g, startColor.b, startColor.a); + rlVertex2f(bottomLeft.x, bottomLeft.y); + + // Second triangle + rlColor4ub(startColor.r, startColor.g, startColor.b, startColor.a); + rlVertex2f(bottomLeft.x, bottomLeft.y); + rlColor4ub(endColor.r, endColor.g, endColor.b, endColor.a); + rlVertex2f(topRight.x, topRight.y); + rlColor4ub(endColor.r, endColor.g, endColor.b, endColor.a); + rlVertex2f(bottomRight.x, bottomRight.y); + rlEnd(); +} \ No newline at end of file diff --git a/shared/src/values/Similarity.cpp b/shared/src/values/Similarity.cpp index fcb5a05..0b0bc74 100644 --- a/shared/src/values/Similarity.cpp +++ b/shared/src/values/Similarity.cpp @@ -1,56 +1,88 @@ #include "values/Similarity.hpp" #include +#include +#include +#include +#include namespace Similarity { + + float dot_minmax(Dna *d1, Dna *d2) + { + uint64_t max = sizeof(Dna) * 255 * 255; + uint8_t *a = (uint8_t *)d1; + uint8_t *b = (uint8_t *)d2; + uint32_t result = 0; + for (size_t i = 0; i < sizeof(Dna); ++i) + { + result += static_cast(a[i]) * static_cast(b[i]); + } + + return result / (double)max; + } + + float euclidean_distance(Dna *d1, Dna *d2) + { + uint8_t *a = (uint8_t *)d1; + uint8_t *b = (uint8_t *)d2; + float sum = 0.0f; + for (size_t i = 0; i < sizeof(Dna); ++i) + { + float diff = static_cast(a[i]) - static_cast(b[i]); + sum += diff * diff; + } + + float distance = std::sqrt(sum); + float max_distance = 255.0f * std::sqrt(static_cast(sizeof(Dna))); + return 1 - (distance / max_distance); + } + // todo: use int8_t insted of uint8_t and map data // 0 -> -128 // 255 -> 127 // int8_t = uint8_t - 128 - // float cosine_similarity(Dna *d1, Dna *d2) - // { - // uint8_t *d1a = (uint8_t *)d1; - // uint8_t *d2a = (uint8_t *)d2; + float cosine_similarity(Dna *d1, Dna *d2) + { + uint8_t *d1a = (uint8_t *)d1; + uint8_t *d2a = (uint8_t *)d2; - // float mag1 = 0.0f; - // float mag2 = 0.0f; - // float dot_prod = 0.0f; - // for (size_t i = 0; i < sizeof(Dna); i++) - // { - // dot_prod += d1a[i] * d2a[i]; - // mag1 += d1a[i] * d1a[i]; - // mag2 += d2a[i] * d2a[i]; - // } - // mag1 = sqrt(mag1); - // mag2 = sqrt(mag2); + float mag1 = 0.0f; + float mag2 = 0.0f; + float dot_prod = 0.0f; + for (size_t i = 0; i < sizeof(Dna); i++) + { + dot_prod += d1a[i] * d2a[i]; + mag1 += d1a[i] * d1a[i]; + mag2 += d2a[i] * d2a[i]; + } + mag1 = sqrt(mag1); + mag2 = sqrt(mag2); - // return dot_prod / (mag1 * mag2); - // } + return dot_prod / (mag1 * mag2); + } - // float cosine_similarity_int(Dna *d1, Dna *d2) - // { - // auto map = [](uint8_t a) -> int8_t - // { return a - 128; }; - - // uint8_t *d1a = (uint8_t *)d1; - // uint8_t *d2a = (uint8_t *)d2; - - // float mag1 = 0.0f; - // float mag2 = 0.0f; - // float dot_prod = 0.0f; - // for (size_t i = 0; i < sizeof(Dna); i++) - // { - // int8_t a = map(d1a[i]); - // int8_t b = map(d2a[i]); - // dot_prod += a * b; - // mag1 += a * a; - // mag2 += b * b; - // } - // mag1 = sqrt(mag1); - // mag2 = sqrt(mag2); - - // return dot_prod / (mag1 * mag2); - // } + float cosine_similarity_int(Dna *d1, Dna *d2) + { + auto map = [](uint8_t a) -> int8_t + { return a - 128; }; + uint8_t *d1a = (uint8_t *)d1; + uint8_t *d2a = (uint8_t *)d2; + float mag1 = 0.0f; + float mag2 = 0.0f; + float dot_prod = 0.0f; + for (size_t i = 0; i < sizeof(Dna); i++) + { + int8_t a = map(d1a[i]); + int8_t b = map(d2a[i]); + dot_prod += a * b; + mag1 += a * a; + mag2 += b * b; + } + mag1 = sqrt(mag1); + mag2 = sqrt(mag2); + return dot_prod / (mag1 * mag2); + } float hamming_distance(Dna *d1, Dna *d2) { @@ -84,8 +116,41 @@ namespace Similarity return 1 - (distance / (end - start)); } + const char *nameofFunc(simil_func f) + { + if (f == &Similarity::euclidean_distance) + { + return "eucl"; + } + else if (f == &Similarity::dot_minmax) + { + return "dot"; + } + else if (f == &Similarity::cosine_similarity) + { + return "cos"; + } + else if (f == &Similarity::cosine_similarity_int) + { + return "cos_i"; + } + else if (f == &Similarity::hamming_distance) + { + return "hamming"; + } + else if (f == &Similarity::levenshtein_distance) + { + return "leven"; + } + else + { + return "unknown"; + } + } + float calc_similarity(std::vector &vec, simil_func f) { + auto start = std::chrono::high_resolution_clock::now(); size_t num_pairs = (vec.size() * (vec.size() - 1)) / 2; float total_similarity = 0.0; @@ -97,6 +162,48 @@ namespace Similarity } } float average_similarity = total_similarity / num_pairs; + + auto stop = std::chrono::high_resolution_clock::now(); + + const auto int_ms = std::chrono::duration_cast(stop - start); + + TraceLog(LOG_INFO, "%s, %d", nameofFunc(f), int_ms); return average_similarity * 100.0f; } + + float levenshtein_distance(Dna *d1, Dna *d2) + { + size_t len = sizeof(Dna); + uint8_t *a = (uint8_t *)d1; + uint8_t *b = (uint8_t *)d2; + + // Create a distance matrix + static std::vector> dp(len + 1, std::vector(len + 1, 0)); + + // Initialize the first row and column + for (size_t i = 0; i <= len; ++i) + { + dp[i][0] = i; + } + for (size_t j = 0; j <= len; ++j) + { + dp[0][j] = j; + } + + // Fill the distance matrix + for (size_t i = 1; i <= len; ++i) + { + for (size_t j = 1; j <= len; ++j) + { + uint32_t cost = (a[i - 1] == b[j - 1]) ? 0 : 1; + dp[i][j] = std::min({ + dp[i - 1][j] + 1, // deletion + dp[i][j - 1] + 1, // insertion + dp[i - 1][j - 1] + cost // substitution + }); + } + } + return 1 - (dp[len][len] / float(len + len)); + } + } diff --git a/view/inc/Vapp.hpp b/view/inc/Vapp.hpp index e5fda0f..506ccee 100644 --- a/view/inc/Vapp.hpp +++ b/view/inc/Vapp.hpp @@ -11,10 +11,11 @@ enum DrawingStage drawTree, drawBig, calSim, + save, done, }; -constexpr int numberOfFunc = 2; +constexpr int numberOfFunc = 6; class Vapp { @@ -52,4 +53,5 @@ private: std::vector> similTable; void setUpTable(); + void drawToFile(); }; \ No newline at end of file diff --git a/view/src/Vapp.cpp b/view/src/Vapp.cpp index c50ec59..5231658 100644 --- a/view/src/Vapp.cpp +++ b/view/src/Vapp.cpp @@ -6,12 +6,13 @@ #include #include #include +#include const char select_user_id[] = "SELECT USER_ID FROM user_table GROUP BY USER_ID;"; constexpr int sizeOfCanvas = 1000; -void Vapp::init(char* filename) +void Vapp::init(char *filename) { bigTexture = LoadRenderTexture(sizeOfCanvas * 4, sizeOfCanvas * 4); treeTexture = LoadRenderTexture(sizeOfCanvas, sizeOfCanvas); @@ -100,11 +101,18 @@ void Vapp::update() break; case DrawingStage::calSim: - simil[0] = Similarity::calc_similarity(manager.vector, Similarity::hamming_distance); - simil[1] = Similarity::calc_similarity(manager.vector, Similarity::hamming_distance_without_seeds); + simil[0] = Similarity::calc_similarity(manager.vector, Similarity::euclidean_distance); + simil[1] = Similarity::calc_similarity(manager.vector, Similarity::cosine_similarity); + simil[2] = Similarity::calc_similarity(manager.vector, Similarity::cosine_similarity_int); + simil[3] = Similarity::calc_similarity(manager.vector, Similarity::hamming_distance); + simil[4] = Similarity::calc_similarity(manager.vector, Similarity::levenshtein_distance); + simil[5] = Similarity::calc_similarity(manager.vector, Similarity::dot_minmax); + stageOfDrawing = DrawingStage::save; + break; + case DrawingStage::save: + drawToFile(); stageOfDrawing = DrawingStage::done; break; - case DrawingStage::done: enableAll = true; break; @@ -173,13 +181,35 @@ void Vapp::draw() if (showStats) { ImGui::Begin("Status", &showStats); - ImGui::LabelText("##sim1", "hamming_distance: %f", simil[0]); - ImGui::LabelText("##sim2", "hamming_distance_without_seeds: %f", simil[1]); + ImGui::LabelText("##sim1", "euclidean_distance: %f", simil[0]); + ImGui::LabelText("##sim2", "cosine_similarity: %f", simil[1]); + ImGui::LabelText("##sim3", "cosine_similarity_int: %f", simil[2]); + ImGui::LabelText("##sim4", "hamming_distance: %f", simil[3]); + ImGui::LabelText("##sim5", "levenshtein_distance: %f", simil[4]); + ImGui::LabelText("##sim6", "dot_minmax: %f", simil[5]); const ImGuiTableFlags flags = ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ContextMenuInBody; const int columns = numberOfFunc + 1; if (ImGui::BeginTable("table1", columns, flags)) { + + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex(0); + ImGui::Text("index"); + ImGui::TableSetColumnIndex(1); + ImGui::Text("euclidean_distance"); + ImGui::TableSetColumnIndex(2); + ImGui::Text("cosine_similarity"); + ImGui::TableSetColumnIndex(3); + ImGui::Text("cosine_similarity_int"); + ImGui::TableSetColumnIndex(4); + ImGui::Text("hamming_distance"); + ImGui::TableSetColumnIndex(5); + ImGui::Text("levenshtein_distance"); + ImGui::TableSetColumnIndex(6); + ImGui::Text("dot_minmax"); + for (int row = 0; row < similTable.size(); row++) { ImGui::TableNextRow(); @@ -277,7 +307,7 @@ void Vapp::setUpTable() UiUnit unit = DnaManager::next(&manager); if ((unit.index != pos)) { - // DOTO: SET ERROR + // TODO: SET ERROR TraceLog(LOG_ERROR, "LOADING DNA"); sql::finalize(get_gen_stmt); return; @@ -290,9 +320,13 @@ void Vapp::setUpTable() { similTable.emplace_back(); int s = similTable.size() - 1; - similTable[s][0] = Similarity::calc_similarity(manager.vector, Similarity::hamming_distance); - similTable[s][1] = Similarity::calc_similarity(manager.vector, Similarity::hamming_distance_without_seeds); + similTable[s][0] = Similarity::calc_similarity(manager.vector, Similarity::euclidean_distance); + similTable[s][1] = Similarity::calc_similarity(manager.vector, Similarity::cosine_similarity); + similTable[s][2] = Similarity::calc_similarity(manager.vector, Similarity::cosine_similarity_int); + similTable[s][3] = Similarity::calc_similarity(manager.vector, Similarity::hamming_distance); + similTable[s][4] = Similarity::calc_similarity(manager.vector, Similarity::levenshtein_distance); + similTable[s][5] = Similarity::calc_similarity(manager.vector, Similarity::dot_minmax); DnaManager::newGen(&manager); } else @@ -301,7 +335,37 @@ void Vapp::setUpTable() } sql::reset(get_gen_stmt); + + int64_t id = ids[selected_id_index]; + char buff[50]; + sprintf(buff, "%ld.txt", id); + std::ofstream file(buff); + + file << "| index | euclidean_distance | cosine_similarity | cosine_similarity_int | hamming_distance | levenshtein_distance | dot_minmax |\n"; + file << "| --- | --- | --- | --- | --- | --- | --- |\n"; + + + + for (size_t i = 0; i < similTable.size(); i++) + { + file << "|" << i << "|"; + for (size_t j = 0; j < similTable[i].size(); j++) + { + file << similTable[i][j] << "|"; + } + file << "\n"; + } } sql::finalize(get_gen_stmt); +} + +void Vapp::drawToFile() +{ + int64_t id = ids[selected_id_index]; + char buff[50]; + sprintf(buff, "%ld_%d.png", id, selected_gen); + Image image = LoadImageFromTexture(bigTexture.texture); + ExportImage(image, buff); + UnloadImage(image); } \ No newline at end of file