#include "fark_karsilastirma.hpp" #include #include #include #include #include "fark_stil.hpp" #include "fiko/analiz/analiz_sistemi.hpp" #include "fiko/sahne/sahne.hpp" #include "imgui.h" namespace fiko { namespace { using namespace farkstil; constexpr const char* kKipler[] = {"Analiz (Silinen)", "Analiz (Eklenen)", "Eklenen + Silinen", "Orijinal Eski", "Orijinal Yeni"}; struct MiniKamera { f32 yaw = 0.7853982f, pitch = 0.6154797f, zoom = 1.0f; Vec3f merkez{}; }; struct KarsDurum { bool acik = false; u32 analiz = 0; int kipA = 0, kipB = 1; bool sync = true; bool fitGerek = true; MiniKamera kamA, kamB; }; KarsDurum& durum() { static KarsDurum d; return d; } void kameraBaz(const MiniKamera& k, Vec3f& sag, Vec3f& yukari, Vec3f& ileri) { const f32 cy = std::cos(k.yaw), sy = std::sin(k.yaw); const f32 cp = std::cos(k.pitch), sp = std::sin(k.pitch); ileri = {cp * cy, cp * sy, sp}; sag = Vec3f{-sy, cy, 0}.birim(); yukari = sag.capraz(ileri * -1.0f).birim(); if (yukari.z < 0) yukari = yukari * -1.0f; } ImU32 renk32(Vec3f c, f32 p) { const auto k = [&](f32 x) { return (int)(std::clamp(x * p, 0.0f, 1.0f) * 255.0f); }; return IM_COL32(k(c.x), k(c.y), k(c.z), 255); } // Sapma → renk: main ile AYNI formül. gradyan=true → SAME→turuncu→degisim (heatmap); // false → binary (tolerans üstü degisim rengi). renkKip 2 (orijinal) → düz malzeme. Vec3f sapmaRenk(f32 d, f32 tol, bool gradyan, Vec3f ayni, Vec3f degisim) { constexpr Vec3f turuncu{1.0f, 0.55f, 0.0f}; const auto lerp = [](Vec3f a, Vec3f b, f32 t) -> Vec3f { return {a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t}; }; if (gradyan) { if (d <= 1e-9f) return ayni; const f32 t = std::max(tol, 1e-6f); return d <= t ? lerp(ayni, turuncu, d / t) : lerp(turuncu, degisim, std::min((d - t) / (4.0f * t), 1.0f)); } return d > tol ? degisim : ayni; } struct PU { // painter üçgen: 3 ekran noktası + 3 vertex rengi + derinlik ImVec2 e[3]; ImU32 c[3]; f32 z; }; std::vector& scratch() { static std::vector v; return v; } // Bir gövdeyi PER-VERTEX renkli üçgenlerle çiz (Gouraud — pürüzsüz ısı-haritası). void govdeTopla(const MiniKamera& kam, ImVec2 vp0, ImVec2 vpBoy, Dugum* d, const std::vector* sapma, f32 tol, bool gradyan, bool orijinal, Vec3f ayni, Vec3f degisim, std::vector& out) { if (!d || d->cpuKonum.empty() || d->cpuIndeks.empty()) return; Vec3f sag, yukari, ileri; kameraBaz(kam, sag, yukari, ileri); const ImVec2 orta{vp0.x + vpBoy.x * 0.5f, vp0.y + vpBoy.y * 0.55f}; const Donusumd& tr = d->donusum; const Vec3f isik = (ileri + Vec3f{0.35f, 0.25f, 0.6f}).birim(); const bool sapmaVar = sapma && sapma->size() == d->cpuKonum.size(); const size_t nUcgen = d->cpuIndeks.size() / 3; const size_t adim = std::max(1, nUcgen / 16000); // ~16k üçgen tavanı (akıcı) Vec3f wp[3]; ImVec2 ep[3]; f32 zp[3]; for (size_t t = 0; t < nUcgen; t += adim) { const u32 idx[3] = {d->cpuIndeks[t * 3], d->cpuIndeks[t * 3 + 1], d->cpuIndeks[t * 3 + 2]}; bool disi = true; for (int i = 0; i < 3; ++i) { const Vec3d w = tr.noktaUygula( {(f64)d->cpuKonum[idx[i]].x, (f64)d->cpuKonum[idx[i]].y, (f64)d->cpuKonum[idx[i]].z}); wp[i] = {(f32)w.x, (f32)w.y, (f32)w.z}; const Vec3f v = wp[i] - kam.merkez; zp[i] = v.nokta(ileri); ep[i] = {orta.x + v.nokta(sag) * kam.zoom, orta.y - v.nokta(yukari) * kam.zoom}; if (ep[i].x >= vp0.x && ep[i].x <= vp0.x + vpBoy.x && ep[i].y >= vp0.y && ep[i].y <= vp0.y + vpBoy.y) disi = false; } if (disi) continue; const Vec3f n = (wp[1] - wp[0]).capraz(wp[2] - wp[0]).birim(); const f32 par = 0.5f + 0.5f * std::abs(n.nokta(isik)); PU pu; for (int i = 0; i < 3; ++i) { pu.e[i] = ep[i]; Vec3f taban; if (orijinal) taban = idx[i] < d->cpuRenk.size() ? d->cpuRenk[idx[i]] : d->renk; else taban = sapmaRenk(sapmaVar ? (*sapma)[idx[i]] : 0.0f, tol, gradyan, ayni, degisim); pu.c[i] = renk32(taban, par); } pu.z = (zp[0] + zp[1] + zp[2]) * (1.0f / 3.0f); out.push_back(pu); } } void kenarCiz(ImDrawList* dl, const MiniKamera& kam, ImVec2 vp0, ImVec2 vpBoy, Dugum* d, Vec3f kenarRenk) { if (!d || d->cpuKenar.empty()) return; Vec3f sag, yukari, ileri; kameraBaz(kam, sag, yukari, ileri); const ImVec2 orta{vp0.x + vpBoy.x * 0.5f, vp0.y + vpBoy.y * 0.55f}; const Donusumd& tr = d->donusum; const ImU32 rk = renk32(kenarRenk, 1.0f); const size_t cift = d->cpuKenar.size() / 2; const size_t adim = std::max(1, cift / 9000); for (size_t i = 0; i < cift; i += adim) { ImVec2 uv[2]; for (int j = 0; j < 2; ++j) { const Vec3f& p = d->cpuKenar[i * 2 + j]; const Vec3d w = tr.noktaUygula({(f64)p.x, (f64)p.y, (f64)p.z}); const Vec3f v = Vec3f{(f32)w.x, (f32)w.y, (f32)w.z} - kam.merkez; uv[j] = {orta.x + v.nokta(sag) * kam.zoom, orta.y - v.nokta(yukari) * kam.zoom}; } if ((uv[0].x < vp0.x && uv[1].x < vp0.x) || (uv[0].x > vp0.x + vpBoy.x && uv[1].x > vp0.x + vpBoy.x) || (uv[0].y < vp0.y && uv[1].y < vp0.y) || (uv[0].y > vp0.y + vpBoy.y && uv[1].y > vp0.y + vpBoy.y)) continue; dl->AddLine(uv[0], uv[1], rk, 1.0f); } } void sigdir(MiniKamera& kam, Sahne& sahne, u32 eski, u32 yeni, ImVec2 vpBoy) { Aabbf kutu; for (u32 g : {eski, yeni}) { Dugum* d = g ? sahne.bul(g) : nullptr; if (!d) continue; const Vec3f m = d->dunyaMerkez(); const f32 r = d->dunyaYaricap(); kutu.genislet(m - Vec3f{r, r, r}); kutu.genislet(m + Vec3f{r, r, r}); } if (!kutu.gecerliMi()) return; kam.merkez = kutu.merkez(); kam.zoom = std::min(vpBoy.x, vpBoy.y) / (std::max(kutu.kosegen(), 1e-3f) * 0.85f); } // Tek viewport: gradyan zemin + grid + Gouraud model + kenar + eksen üçlüsü + orbit. void viewportCiz(Sahne& sahne, analiz::AnalizNesnesi& a, MiniKamera& kam, int kip, const char* id, ImVec2 boyut) { ImDrawList* dl = ImGui::GetWindowDrawList(); const ImVec2 p0 = ImGui::GetCursorScreenPos(); const ImVec2 p1{p0.x + boyut.x, p0.y + boyut.y}; dl->AddRectFilledMultiColor(p0, p1, IM_COL32(0x33, 0x33, 0x46, 255), IM_COL32(0x33, 0x33, 0x46, 255), IM_COL32(0x18, 0x18, 0x24, 255), IM_COL32(0x18, 0x18, 0x24, 255)); dl->PushClipRect(p0, p1, true); const ImU32 grid = IM_COL32(255, 255, 255, 9); for (float x = p0.x; x < p1.x; x += 24.0f) dl->AddLine({x, p0.y}, {x, p1.y}, grid); for (float y = p0.y; y < p1.y; y += 24.0f) dl->AddLine({p0.x, y}, {p1.x, y}, grid); Dugum* eski = a.farkKarsiGovde ? sahne.bul(a.farkKarsiGovde) : nullptr; Dugum* yeni = a.hedefGovde ? sahne.bul(a.hedefGovde) : nullptr; const f32 tol = a.farkToleransCozulmus > 0 ? a.farkToleransCozulmus : 1.0f; const bool grad = a.farkGorunum == 0; std::vector& u = scratch(); u.clear(); const bool orij = (kip == 3 || kip == 4); const auto silinen = [&] { govdeTopla(kam, p0, boyut, eski, &a.farkSonucEski.deger, tol, grad, orij, a.farkRenkAyni, a.farkRenkSilinen, u); }; const auto eklenen = [&] { govdeTopla(kam, p0, boyut, yeni, &a.sonuc.deger, tol, grad, orij, a.farkRenkAyni, a.farkRenkEklenen, u); }; if (kip == 0 || kip == 3) silinen(); else if (kip == 1 || kip == 4) eklenen(); else { silinen(); eklenen(); } std::sort(u.begin(), u.end(), [](const PU& x, const PU& y) { return x.z > y.z; }); // Per-vertex renkli üçgen (Gouraud): PrimReserve/PrimVtx düşük seviye. const ImVec2 uv = ImGui::GetFontTexUvWhitePixel(); dl->PrimReserve((int)u.size() * 3, (int)u.size() * 3); for (const PU& t : u) { dl->PrimVtx(t.e[0], uv, t.c[0]); dl->PrimVtx(t.e[1], uv, t.c[1]); dl->PrimVtx(t.e[2], uv, t.c[2]); } if (a.farkKenarGoster) { if (kip == 0 || kip == 2 || kip == 3) kenarCiz(dl, kam, p0, boyut, eski, a.farkRenkKenar); if (kip == 1 || kip == 2 || kip == 4) kenarCiz(dl, kam, p0, boyut, yeni, a.farkRenkKenar); } // Sol-alt eksen üçlüsü (kamera ile döner). { Vec3f sag, yukari, ileri; kameraBaz(kam, sag, yukari, ileri); const ImVec2 em{p0.x + 26.0f, p1.y - 26.0f}; const auto eksen = [&](Vec3f yon, ImU32 renk, const char* ad) { const ImVec2 uc{em.x + yon.nokta(sag) * 16.0f, em.y - yon.nokta(yukari) * 16.0f}; dl->AddLine(em, uc, renk, 1.6f); dl->AddText({uc.x + 1, uc.y - 6}, renk, ad); }; eksen({1, 0, 0}, IM_COL32(235, 80, 80, 220), "x"); eksen({0, 1, 0}, IM_COL32(80, 220, 100, 220), "y"); eksen({0, 0, 1}, IM_COL32(90, 140, 250, 220), "z"); } dl->PopClipRect(); ImGui::SetCursorScreenPos(p0); ImGui::InvisibleButton(id, boyut); if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left)) { const ImVec2 dd = ImGui::GetIO().MouseDelta; kam.yaw -= dd.x * 0.01f; kam.pitch = std::clamp(kam.pitch + dd.y * 0.01f, -1.5f, 1.5f); } if (ImGui::IsItemHovered() && ImGui::GetIO().MouseWheel != 0.0f) kam.zoom *= std::pow(1.15f, ImGui::GetIO().MouseWheel); } static void aspektKisit(ImGuiSizeCallbackData* d) { d->DesiredSize.y = std::max(d->DesiredSize.y, d->DesiredSize.x * 0.82f); } } // namespace void farkKarsilastirmaAc(u32 analizKimlik) { KarsDurum& d = durum(); d.acik = true; d.analiz = analizKimlik; d.fitGerek = true; } bool farkKarsilastirmaAcikMi() { return durum().acik; } void farkKarsilastirmaCiz(Sahne& sahne, analiz::AnalizSistemi& an) { KarsDurum& du = durum(); if (!du.acik) return; analiz::AnalizNesnesi* a = an.bul(du.analiz); if (!a || a->tip() != analiz::AnalizTip::Fark) { du.acik = false; return; } const int tema = acikTemaBasla(); ImGui::SetNextWindowSize({560, 560}, ImGuiCond_FirstUseEver); ImGui::SetNextWindowSizeConstraints({420, 380}, {2400, 1800}, aspektKisit); bool acik = true; if (ImGui::Begin("CAD Farklar - Karşılaştırma###farkKarsilastirma", &acik)) { turkuazGlowCiz(); aeroGridCiz(); ImGui::TextColored(ImVec4(0.35f, 0.95f, 0.90f, 1.0f), "⇄ CAD Farklar - Karşılaştırma"); if (a->sonuc.hazir) { ImGui::SameLine(ImGui::GetContentRegionAvail().x - 64.0f); ImGui::TextDisabled("| %.0fms", a->farkSureMs); } ImGui::Separator(); { kartBaslat("modelGorunumKart"); bolumBaslik("Model Görünümü"); const float yariW = (ImGui::GetContentRegionAvail().x - 6.0f) * 0.5f; const float altPay = a->sonuc.hazir ? 300.0f : 190.0f; const float vpY = std::max(120.0f, ImGui::GetWindowHeight() - altPay); if (du.fitGerek) { sigdir(du.kamA, sahne, a->farkKarsiGovde, a->hedefGovde, {yariW, vpY}); du.kamB = du.kamA; du.fitGerek = false; } // İki panel EXPLICIT geometri (BeginGroup/SameLine cursor-drift YOK → // ikisi de BİREBİR aynı y + vpY yükseklik). Satır sol-üstü sabitlenir. const ImVec2 rowSol = ImGui::GetCursorScreenPos(); const float barY = 24.0f; ImDrawList* dl = ImGui::GetWindowDrawList(); for (int panel = 0; panel < 2; ++panel) { const float bx = rowSol.x + panel * (yariW + 6.0f); // Üst bar (combo zemini) + A/B rozeti. dl->AddRectFilled({bx, rowSol.y}, {bx + yariW, rowSol.y + barY}, IM_COL32(0xf0, 0xf0, 0xf0, 255), 3.0f); dl->AddRectFilled({bx + 4, rowSol.y + 4}, {bx + 20, rowSol.y + 20}, panel == 0 ? kRozetA : kRozetB, 2.0f); ImGui::SetCursorScreenPos({bx + 24, rowSol.y + 2}); ImGui::SetNextItemWidth(yariW - 28.0f); int& kip = panel == 0 ? du.kipA : du.kipB; ImGui::Combo(panel == 0 ? "##kipA" : "##kipB", &kip, kKipler, 5); // Viewport: aynı y (rowSol.y+26) + aynı vpY yükseklik → panel A ve B EŞİT. ImGui::SetCursorScreenPos({bx, rowSol.y + barY + 2.0f}); MiniKamera& kam = (du.sync || panel == 0) ? du.kamA : du.kamB; viewportCiz(sahne, *a, kam, kip, panel == 0 ? "##vpA" : "##vpB", {yariW, vpY}); } // İki panelden sonra imleci satırın ALTINA taşı (lejant vs. akışa devam). ImGui::SetCursorScreenPos({rowSol.x, rowSol.y + barY + 2.0f + vpY + 4.0f}); // Lejant. ImGui::Dummy({0, 4}); const auto lejant = [&](Vec3f renk, const char* ad) { ImDrawList* dl = ImGui::GetWindowDrawList(); const ImVec2 lp = ImGui::GetCursorScreenPos(); dl->AddRectFilled({lp.x, lp.y + 2}, {lp.x + 20, lp.y + 14}, renk32(renk, 1.0f), 2.0f); dl->AddRect({lp.x, lp.y + 2}, {lp.x + 20, lp.y + 14}, IM_COL32(0x99, 0x99, 0x99, 255), 2.0f); ImGui::Dummy({22, 16}); ImGui::SameLine(); ImGui::TextDisabled("%s", ad); }; ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x - 250.0f) * 0.5f); lejant(a->farkRenkAyni, "Aynı"); ImGui::SameLine(0, 16); lejant(a->farkRenkSilinen, "Silinen"); ImGui::SameLine(0, 16); lejant(a->farkRenkEklenen, "Eklenen"); kartBitir(); } if (a->sonuc.hazir) { kartBaslat("karsIstatistik"); const size_t nT = a->sonuc.deger.size() + a->farkSonucEski.deger.size(); const f32 eslesme = nT ? 100.0f * (1.0f - (f32)(a->farkEklenen + a->farkSilinen) / (f32)nT) : 100.0f; const auto satir = [&](const char* e, const char* v, ImU32 r) { ImGui::TextDisabled("%s", e); ImGui::SameLine(ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(v).x); ImGui::TextColored(ImGui::ColorConvertU32ToFloat4(r), "%s", v); }; char v[48]; std::snprintf(v, sizeof(v), "%.1f%%", eslesme); satir("Eşleşme:", v, kYesil); std::snprintf(v, sizeof(v), "%u", a->farkSilinen); satir("Silinen nokta:", v, kSilinenMetin); std::snprintf(v, sizeof(v), "%u", a->farkEklenen); satir("Eklenen nokta:", v, kEklenenMetin); if (a->farkP99 > 0.0f) { std::snprintf(v, sizeof(v), "%.3f mm", a->farkP99); satir("Maks. sapma (P99):", v, IM_COL32(0x33, 0x33, 0x33, 255)); std::snprintf(v, sizeof(v), "%.3f mm", a->farkOrt); satir("Ort. sapma:", v, IM_COL32(0x33, 0x33, 0x33, 255)); } kartBitir(); } ImGui::SetCursorPosX((ImGui::GetContentRegionAvail().x - 3 * 30.0f) * 0.5f); if (ImGui::Button("↺##kamSifir", {26, 0})) du.fitGerek = true; if (ImGui::IsItemHovered()) ImGui::SetTooltip("Kamerayı Sıfırla"); ImGui::SameLine(); if (du.sync) ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.91f, 0.96f, 0.99f, 1)); if (ImGui::Button("⟳##senk", {26, 0})) { du.sync = !du.sync; if (du.sync) du.kamB = du.kamA; } if (du.sync) ImGui::PopStyleColor(); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Senkronizasyon"); ImGui::SameLine(); if (ImGui::Button("⛶##sigdir", {26, 0})) du.fitGerek = true; if (ImGui::IsItemHovered()) ImGui::SetTooltip("Ekrana Sığdır"); } ImGui::End(); acikTemaBitir(tema); if (!acik) du.acik = false; } } // namespace fiko