#include "analiz_penceresi.hpp" #include "fark_karsilastirma.hpp" #include "fark_panel.hpp" #include #include #include #include #include #include #include "fiko/analiz/analiz_sistemi.hpp" #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/geometri/brep.hpp" #include "fiko/platform/sistem.hpp" #include "fiko/sahne/sahne.hpp" #include "fiko/sahne/sahne_islem.hpp" // pah/fillet önerisi: DuzenleIslem undo #include "fiko/ui/bilesen_ek.hpp" // ui::toastGoster #include "fiko/ui/form.hpp" #include "fiko/ui/pencere_cerceve.hpp" #include "fiko/ui/tema.hpp" #include "belge_ciz.hpp" // kalinlikIsinDurumu — per-fragment ışın teşhisi #include "imgui.h" namespace fiko { namespace { using analiz::AnalizNesnesi; using analiz::AnalizSistemi; using analiz::AnalizTip; using analiz::KesitDuzlemi; std::unordered_map g_anAcik, g_anPin; std::unordered_map g_ksAcik, g_ksPin; bool g_ksListe = false, g_ksListePin = true; // Global kapak (capping) kipi combo — kesit penceresi + listede ortak. // Değişimde tüm kesit kapakları yeniden kurulur. void kapakKipiCombo(AnalizSistemi& an) { const char* kipler[] = {"Kapak yok", "Kapalı (opak)", "Yarı saydam", "Saydam"}; int ki = (int)an.kapakKip; ImGui::SetNextItemWidth(150); if (ImGui::Combo("Kapak", &ki, kipler, 4)) { an.kapakKip = (AnalizSistemi::KapakKip)ki; for (u32 kk : an.kesitler()) an.kesitGuncelle(kk); } if (ImGui::IsItemHovered()) ImGui::SetTooltip("Kesilen iç yüzeyi DOLU göster (veri-renkli kapak).\n" "Kalınlık analizi aktifken kapakta yerel cidar ısı haritası."); } // Normal → eksen combo indeksi (X,Y,Z,-X,-Y,-Z,Serbest). int eksenIndeks(Vec3f n) { const auto esit = [&](f32 x, f32 y, f32 z) { return std::abs(n.x - x) < 1e-4f && std::abs(n.y - y) < 1e-4f && std::abs(n.z - z) < 1e-4f; }; if (esit(1, 0, 0)) return 0; if (esit(0, 1, 0)) return 1; if (esit(0, 0, 1)) return 2; if (esit(-1, 0, 0)) return 3; if (esit(0, -1, 0)) return 4; if (esit(0, 0, -1)) return 5; return 6; } Vec3f eksenNormal(int i) { switch (i) { case 0: return {1, 0, 0}; case 1: return {0, 1, 0}; case 2: return {0, 0, 1}; case 3: return {-1, 0, 0}; case 4: return {0, -1, 0}; default: return {0, 0, -1}; } } ImU32 renkU32(Vec3f c, float a = 1.0f) { return IM_COL32((int)(c.x * 255), (int)(c.y * 255), (int)(c.z * 255), (int)(a * 255)); } // Izometrik projeksiyon (cam_strateji_semasi kalıbı, sade). Dünya → ekran. ImVec2 izo(Vec3f p, ImVec2 orta, float olc) { const float a = 0.61547970867f; // ~35.264° izometrik const float ca = std::cos(a), sa = std::sin(a); float x = (p.x - p.y) * 0.70710678f; float y = (p.x + p.y) * 0.70710678f * sa - p.z * ca; return {orta.x + x * olc, orta.y + y * olc}; } // Pencere-içi 3D önizleme: hedef gövde bbox tel-kafesi + çekim yönü oku. void onizlemeCiz(Sahne& sahne, AnalizNesnesi& a, ImVec2 boyut) { ImDrawList* dl = ImGui::GetWindowDrawList(); const ImVec2 p0 = ImGui::GetCursorScreenPos(); const ImVec2 orta{p0.x + boyut.x * 0.5f, p0.y + boyut.y * 0.5f}; dl->AddRectFilled(p0, {p0.x + boyut.x, p0.y + boyut.y}, IM_COL32(20, 24, 32, 255), 4.0f); Dugum* d = sahne.bul(a.hedefGovde); if (!d) { for (Dugum& n : sahne.dugumler()) if (n.brep && n.indeksSayi > 0) { d = &n; break; } } float olc = 1.0f; Vec3f mrk{0, 0, 0}; if (d) { const Aabbf& s = d->yerelSinir; mrk = s.merkez(); const float diag = s.kosegen(); olc = diag > 1e-3f ? (std::min(boyut.x, boyut.y) * 0.32f) / diag : 1.0f; // Bbox 8 köşe tel-kafes. Vec3f k[8] = { {s.asgari.x, s.asgari.y, s.asgari.z}, {s.azami.x, s.asgari.y, s.asgari.z}, {s.azami.x, s.azami.y, s.asgari.z}, {s.asgari.x, s.azami.y, s.asgari.z}, {s.asgari.x, s.asgari.y, s.azami.z}, {s.azami.x, s.asgari.y, s.azami.z}, {s.azami.x, s.azami.y, s.azami.z}, {s.asgari.x, s.azami.y, s.azami.z}}; ImVec2 e[8]; for (int i = 0; i < 8; ++i) e[i] = izo({k[i].x - mrk.x, k[i].y - mrk.y, k[i].z - mrk.z}, orta, olc); const int kenar[12][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7}}; for (auto& kk : kenar) dl->AddLine(e[kk[0]], e[kk[1]], IM_COL32(90, 130, 170, 200), 1.3f); } // Çekim yönü oku (draft/undercut). if (a.tip() == AnalizTip::Draft || a.tip() == AnalizTip::Undercut) { const Vec3f y = a.cekimYonu; const float L = std::min(boyut.x, boyut.y) * 0.34f; const ImVec2 ub = izo({0, 0, 0}, orta, 1.0f); const ImVec2 uu = izo({y.x * L, y.y * L, y.z * L}, orta, 1.0f); const ImVec2 uc{ub.x + (uu.x - ub.x), ub.y + (uu.y - ub.y)}; dl->AddLine(ub, uc, IM_COL32(255, 220, 60, 255), 2.4f); dl->AddCircleFilled(uc, 4.0f, IM_COL32(255, 220, 60, 255)); dl->AddText({p0.x + 6, p0.y + 6}, IM_COL32(255, 220, 60, 255), "↑ çekim yönü"); } ImGui::Dummy(boyut); } // Rapor satırındaki yüze kamerayı uçur: yüz ordinaline ait mesh noktalarından // DÜNYA AABB'si kurulur (cpuAlt = per-vertex yüz ordinali) → kameraOdak köprüsü. // Köprü bağlı değilse (kontrat kurulmamış) sessizce no-op. void yuzeOdakla(Sahne& sahne, AnalizSistemi& an, AnalizNesnesi& a, u32 ordinal) { if (!an.kameraOdak || !ordinal) return; Dugum* d = sahne.bul(a.hedefGovde); if (!d) { for (Dugum& n : sahne.dugumler()) if (n.brep && n.indeksSayi > 0) { d = &n; break; } } if (!d || d->cpuKonum.empty() || d->cpuAlt.size() != d->cpuKonum.size()) return; const Mat4f model = d->matris(); Aabbf k; for (size_t i = 0; i < d->cpuAlt.size(); ++i) { if (d->cpuAlt[i] != ordinal) continue; const Vec4f w = model * Vec4f{d->cpuKonum[i], 1.0f}; k.genislet({w.x, w.y, w.z}); } if (!k.gecerliMi()) return; const Vec3f m = k.merkez(); // Yarıçap: yüz köşegeni yarısı + pay; çok küçük yüzde kamera burnunu sokmasın. const f32 r = std::max(k.kosegen() * 0.5f, 1e-3f) * 2.2f; an.kameraOdak({m.x, m.y, m.z}, r); } // Sınıf (bant) editörü: renk + [min,max] + nominal ± tolerans satırları. // Radius ve Kalınlık AYNI veri yapısını (AnalizAralik) kullanır → tek editör. // nomBicim: nominal alanının gösterimi ("R%.2f" / "%.2f mm"). genislik: yeni // sınıfın varsayılan bant genişliği. void sinifEditoru(AnalizSistemi& an, AnalizNesnesi& a, const char* baslik, const char* nomBicim, float genislik) { const ImVec4 CYAN = ui::tema().vurgu; ImGui::Spacing(); ImGui::TextColored(CYAN, "%s", baslik); ImGui::SameLine(); bool degisti = false; if (ImGui::SmallButton("+ Sınıf")) { const float taban = a.araliklar.empty() ? 0.f : a.araliklar.back().max; a.araliklar.push_back({taban, taban + genislik, {0.3f + 0.15f * (a.araliklar.size() % 4), 0.7f, 0.5f}, "Sınıf", taban + genislik * 0.5f, 0.1f}); degisti = true; } if (ImGui::IsItemHovered()) ImGui::SetTooltip("Yeni sınıf: renk + [min,max] bandı + nominal ± tolerans"); int silIdx = -1; for (int i = 0; i < (int)a.araliklar.size(); ++i) { auto& b = a.araliklar[i]; ImGui::PushID(i); ImGui::ColorEdit3("##c", &b.renk.x, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel); ImGui::SameLine(); ImGui::SetNextItemWidth(52); ImGui::DragFloat("##min", &b.min, 0.1f, 0.f, 1000.f, "%.1f"); ImGui::SameLine(); ImGui::TextUnformatted("–"); ImGui::SameLine(); ImGui::SetNextItemWidth(52); ImGui::DragFloat("##max", &b.max, 0.1f, 0.f, 1000.f, "%.1f"); ImGui::SameLine(); ImGui::SetNextItemWidth(64); ImGui::DragFloat("##nom", &b.nominal, 0.05f, 0.f, 1000.f, nomBicim); ImGui::SameLine(); ImGui::SetNextItemWidth(52); ImGui::DragFloat("##tol", &b.tolerans, 0.01f, 0.f, 10.f, "±%.2f"); ImGui::SameLine(); if (ImGui::SmallButton("🗑")) silIdx = i; ImGui::PopID(); if (ImGui::IsItemDeactivatedAfterEdit()) degisti = true; } if (silIdx >= 0) { a.araliklar.erase(a.araliklar.begin() + silIdx); degisti = true; } // Sınıflar hesabın GİRDİSİ (bant başına adet/tolerans istatistiği) → yeniden hesap. if (degisti) { a.bayatAyarla(true); an.hesapla(a.kimlik()); } } // Ortak lejant (renk-bar) + basit DrawList histogram. void lejantHistogram(AnalizNesnesi& a) { const ImVec4 SOLUK = ui::tema().metinSoluk; const auto& s = a.sonuc; if (!s.hazir) { ImGui::TextColored(SOLUK, "hesaplanmadı"); return; } ImDrawList* dl = ImGui::GetWindowDrawList(); const ImVec2 p = ImGui::GetCursorScreenPos(); const float w = ImGui::GetContentRegionAvail().x, h = 16.0f; // Renk-bar (analiz tipine göre). const int seg = 48; for (int i = 0; i < seg; ++i) { const float t = (float)i / (seg - 1); Vec3f c; if (a.tip() == AnalizTip::Draft || a.tip() == AnalizTip::Undercut) { const float deg = s.min + t * (s.max - s.min); c = deg < -0.3f ? Vec3f{0.90f, 0.22f, 0.20f} : deg < 0.3f ? Vec3f{0.95f, 0.80f, 0.25f} : Vec3f{0.35f, 0.72f, 0.42f}; } else if (a.tip() == AnalizTip::Fark) { // sapma: mavi(0)→camgöbeği→yeşil→sarı→kırmızı c = t < 0.25f ? Vec3f{0.0f, t * 4.0f, 1.0f} : t < 0.5f ? Vec3f{0.0f, 1.0f, 1.0f - (t - 0.25f) * 4.0f} : t < 0.75f ? Vec3f{(t - 0.5f) * 4.0f, 1.0f, 0.0f} : Vec3f{1.0f, 1.0f - (t - 0.75f) * 4.0f, 0.0f}; } else { // radius / kalınlık / kurvatür: 3B ile AYNI rampa (jet/turbo/tolerans) // Eskiden burada kaba bir kırmızı→yeşil gradyan çiziliyordu; shader ise // rampaTip'e göre jet/turbo boyuyordu → lejant 3B'yi YANLIŞ tarif ediyordu. c = analiz::rampaRenk(a.rampaTip, t); } dl->AddRectFilled({p.x + w * t, p.y}, {p.x + w * (t + 1.0f / seg), p.y + h}, renkU32(c)); } ImGui::Dummy({w, h}); ImGui::Text("%.1f %s", s.min, s.birim.c_str()); ImGui::SameLine(w - 60); ImGui::Text("%.1f %s", s.max, s.birim.c_str()); // Histogram (değer dağılımı — kaç yüz hangi bantta). if (!s.deger.empty()) { const int kova = 20; std::vector hist(kova, 0); int gecerli = 0; for (float v : s.deger) { if (v < -900.0f) continue; // -1 nötr / boş atla float t = (v - s.min) / (s.max - s.min + 1e-6f); t = t < 0 ? 0 : (t > 1 ? 1 : t); hist[(int)(t * (kova - 1))]++; ++gecerli; } int enCok = 1; for (int c : hist) enCok = c > enCok ? c : enCok; const ImVec2 hp = ImGui::GetCursorScreenPos(); const float hw = w / kova, hh = 40.0f; for (int i = 0; i < kova; ++i) { const float bh = hh * (float)hist[i] / enCok; dl->AddRectFilled({hp.x + i * hw, hp.y + hh - bh}, {hp.x + (i + 1) * hw - 1, hp.y + hh}, IM_COL32(90, 170, 220, 220)); } ImGui::Dummy({w, hh}); ImGui::TextColored(SOLUK, "dağılım (%d öğe)", gecerli); } } // Bir sahneden-seçim modu butonu (aktif = cyan basılı; tekrar tık = bitir). void secimButonu(AnalizSistemi& an, u32 kimlik, analiz::SecimKip kip, const char* etiket, std::function gc) { const bool aktif = an.secimAktif() && an.secimKip() == kip; if (aktif) ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{0.08f, 0.42f, 0.55f, 1.0f}); if (ImGui::Button(etiket, {-1, 0})) an.secimBaslat(kimlik, kip, std::move(gc)); if (aktif) ImGui::PopStyleColor(); } // ---- DXF R12 / SVG export (kesit konturu → düzlem u,v) -------------------- // Kesit düzleminin u,v tabanı: normal'e dik iki eksen. void duzlemBaz(Vec3f n, Vec3f& u, Vec3f& v) { n = n.uzunlukKare() > 1e-8f ? n.birim() : Vec3f{0, 0, 1}; Vec3f a = std::abs(n.z) < 0.9f ? Vec3f{0, 0, 1} : Vec3f{1, 0, 0}; u = n.capraz(a).birim(); v = n.capraz(u).birim(); } // Tüm görünür gövdeleri kesip dünya polyline'lar üretir (export için). std::vector> kesitDunyaEgriler(Sahne& sahne, const KesitDuzlemi& k) { std::vector> r; const Vec3f nk = k.nokta(); for (Dugum& d : sahne.dugumler()) { if (!d.brep || !d.brep->gecerliMi() || d.indeksSayi == 0 || !sahne.agacGorunur(d.kimlik)) continue; const Donusumd ters = d.donusum.ters(); const Vec3d ptY = ters.noktaUygula({nk.x, nk.y, nk.z}); const Vec3d nY = ters.yonelim.dondur({k.normal.x, k.normal.y, k.normal.z}); for (auto& poly : geometri::brepKesit(*d.brep, ptY, nY, 0.02)) { std::vector w; w.reserve(poly.size()); for (auto& p : poly) w.push_back(d.donusum.noktaUygula(p)); if (w.size() >= 2) r.push_back(std::move(w)); } } return r; } bool dxfYaz(const std::string& yol, const std::vector>& konturlar) { std::FILE* f = nullptr; if (fopen_s(&f, yol.c_str(), "w") != 0 || !f) return false; std::fprintf(f, "0\nSECTION\n2\nENTITIES\n"); for (const auto& kn : konturlar) for (size_t i = 0; i + 1 < kn.size(); ++i) std::fprintf(f, "0\nLINE\n8\nKESIT\n10\n%.4f\n20\n%.4f\n30\n0.0\n11\n%.4f\n21\n%.4f\n31\n0.0\n", kn[i].x, kn[i].y, kn[i + 1].x, kn[i + 1].y); std::fprintf(f, "0\nENDSEC\n0\nEOF\n"); std::fclose(f); return true; } bool svgYaz(const std::string& yol, const std::vector>& konturlar) { f64 minx = 1e18, miny = 1e18, maxx = -1e18, maxy = -1e18; for (auto& kn : konturlar) for (auto& p : kn) { minx = std::min(minx, p.x); maxx = std::max(maxx, p.x); miny = std::min(miny, p.y); maxy = std::max(maxy, p.y); } if (minx > maxx) return false; const f64 w = maxx - minx, h = maxy - miny; std::FILE* f = nullptr; if (fopen_s(&f, yol.c_str(), "w") != 0 || !f) return false; std::fprintf(f, "\n", w, h, minx, miny, w, h); for (auto& kn : konturlar) { std::fprintf(f, "\n"); } std::fprintf(f, "\n"); std::fclose(f); return true; } // ========================= TEK ANALİZ PENCERESİ =========================== void analizPenceresiCiz(Cekirdek& cekirdek, Sahne& sahne, AnalizSistemi& an, AnalizNesnesi& a) { const ImVec4 CYAN = ui::tema().vurgu; const ImVec4 SOLUK = ui::tema().metinSoluk; const ImVec4 KIRMIZI = ui::tema().hata; bool& ustte = g_anPin[a.kimlik()]; char baslik[160]; std::snprintf(baslik, sizeof(baslik), "%s %s%s###analiz_%u", analiz::analizTipSimge(a.tip()), a.ad().c_str(), a.bayat() ? " *" : "", a.kimlik()); using namespace fiko::ui; using u32 = fiko::u32; // ui::u32(ImVec4) ile tip cakismasini onler bool acik = true, erken = false; // Görünüm: CAM Simülasyon penceresiyle aynı FikoUi WebCam skin'i (teal cam zemin, // cyan kenar, futuristik grid, pill slider + cyan widget ailesi). ui::Pencere pen{baslik, &acik, {.boyut = {330, 0}, .pinDurum = &ustte, .skin = ui::PencereSkin::WebCam}}; if (pen) { if (a.bayat()) { ui::bilgiSeridi(2, "Girdi değişti — Yeniden Hesapla"); } if (sekmeBaslat("##ansek")) { // ── Girdi: hedef gövde + pencere-içi 3D önizleme ── if (sekme("Girdi", "🎯")) { onizlemeCiz(sahne, a, {ImGui::GetContentRegionAvail().x, 150.0f}); ImGui::Text("Hedef: %s", a.hedefGovde && sahne.bul(a.hedefGovde) ? sahne.bul(a.hedefGovde)->ad.c_str() : "(ilk görünür)"); secimButonu(an, a.kimlik(), analiz::SecimKip::Hedef, "🎯 Hedef Gövde Seç", [] {}); sekmeBitir(); } // ── Parametre: analiz-özel girdiler + hesap modu ── if (sekme("Parametre", "⚙")) { ImGui::TextColored(SOLUK, "Parametreler"); // Hesap modu (performans/kesinlik) — mini ⚙ popup. Hızlı = anında GPU görsel, // Kesin = tam CPU sayısal rapor. Draft/Undercut'ta donmayı bitirir. ImGui::SameLine(); ImGui::TextDisabled("· %s", analiz::hesapModAdi(a.hesapMod)); ImGui::SameLine(); if (ImGui::SmallButton("⚙##hesapMod")) ImGui::OpenPopup("hesapModPop"); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Hesap modu (performans / kesinlik)"); if (ImGui::BeginPopup("hesapModPop")) { ImGui::TextColored(CYAN, "Hesap Modu"); ImGui::Separator(); for (int m = 0; m < 3; ++m) { const auto mod = (analiz::HesapMod)m; if (ImGui::RadioButton(analiz::hesapModAdi(mod), a.hesapMod == mod)) { a.hesapMod = mod; a.bayatAyarla(true); an.hesapla(a.kimlik()); } } ImGui::Separator(); if (ImGui::SmallButton("Tümü için varsayılan yap")) analiz::varsayilanHesapModAyarla(a.hesapMod); ImGui::TextColored(SOLUK, "Hızlı: anında GPU görsel (rapor yok).\n" "Kesin: tam CPU per-yüz sayısal rapor.\n" "Hibrit: görsel + arka rapor (yakında)."); ImGui::EndPopup(); } if (a.tip() == AnalizTip::Draft || a.tip() == AnalizTip::Undercut) { ImGui::Text("Çekim Yönü: %.2f %.2f %.2f", a.cekimYonu.x, a.cekimYonu.y, a.cekimYonu.z); if (ImGui::Button("X")) { a.cekimYonu = {1, 0, 0}; a.bayatAyarla(true); an.hesapla(a.kimlik()); } ImGui::SameLine(); if (ImGui::Button("Y")) { a.cekimYonu = {0, 1, 0}; a.bayatAyarla(true); an.hesapla(a.kimlik()); } ImGui::SameLine(); if (ImGui::Button("Z")) { a.cekimYonu = {0, 0, 1}; a.bayatAyarla(true); an.hesapla(a.kimlik()); } ImGui::SameLine(); // Eksen butonları yalnız +X/+Y/+Z veriyordu → −Z (kalıbın diğer yarısı) seçilemiyordu. if (ImGui::Button("⇅")) { a.cekimYonu = a.cekimYonu * -1.f; a.bayatAyarla(true); an.hesapla(a.kimlik()); } if (ImGui::IsItemHovered()) ImGui::SetTooltip("Çekim yönünü tersine çevir (kalıbın diğer yarısı)"); ImGui::SameLine(); secimButonu(an, a.kimlik(), analiz::SecimKip::CekimYonu, "🎯 Yüzeyden", [] {}); } else if (a.tip() == AnalizTip::Radius) { float e = a.radiusEsik; ui::alan("Min Yarıçap Eşiği"); if (ImGui::DragFloat("##radiusEsik", &e, 0.1f, 0.1f, 50.0f, "%.2f mm")) { a.radiusEsik = e; a.bayatAyarla(true); } if (ImGui::IsItemDeactivatedAfterEdit()) an.hesapla(a.kimlik()); sinifEditoru(an, a, "Yarıçap Sınıfları", "R%.2f", 5.0f); } else if (a.tip() == AnalizTip::Kalinlik) { float e = a.kalinlikInce; ui::alan("İnce Eşik"); if (ImGui::DragFloat("##kalinlikInce", &e, 0.1f, 0.1f, 50.0f, "%.2f mm")) { a.kalinlikInce = e; a.bayatAyarla(true); } if (ImGui::IsItemDeactivatedAfterEdit()) an.hesapla(a.kimlik()); // Boyama yolu teşhisi: per-fragment ışın aktif değilse ÜÇGEN DESENİ görülür — // sebebini tahmin ettirmek yerine burada yaz. { u32 ucgen = 0; const char* durum = kalinlikIsinDurumu(&ucgen); const bool aktif = std::string(durum) == "AKTİF"; if (aktif) ImGui::TextColored(ImVec4(0.35f, 0.95f, 0.55f, 1.f), "⚡ Piksel ışın AKTİF (%u üçgen)", ucgen); else ImGui::TextColored(ImVec4(1.f, 0.75f, 0.25f, 1.f), "⚠ Piksel ışın kapalı: %s", durum); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Aktifken kalınlık HER PİKSEL için ışınla ölçülür (üçgen deseni yok).\n" "Kapalıyken per-vertex alan kullanılır → büyük/delikli yüzlerde " "köşelerden yayılan üçgen deseni görünebilir."); } // Kalınlık sınıfları: birden çok hedef ölçü (ör. 3±0.2, 5±0.3). Tanımlıysa 3B // boyama sürekli rampa yerine AYRIK sınıf renklerine geçer (per-fragment ışın // ile piksel hassasiyetinde); hiçbir sınıfa girmeyen bölge nötr gri kalır. sinifEditoru(an, a, "Kalınlık Sınıfları", "%.2f mm", 2.0f); } else if (a.tip() == AnalizTip::Kurvatur) { // Egrilik ölçüsü seçimi (Gauss / Ortalama / Maks mutlak) + rampa ucu. const char* modlar[] = {"Gauss (K)", "Ortalama (H)", "Maks |k|"}; int mi = (int)a.kurvaturMod; ui::alan("Ölçü"); if (ImGui::Combo("##kurvaturMod", &mi, modlar, 3)) { a.kurvaturMod = (analiz::AnalizNesnesi::KurvaturMod)mi; a.bayatAyarla(true); an.hesapla(a.kimlik()); } float e = a.kurvaturMaks; ui::alan("Eğrilik Maks"); if (ImGui::DragFloat("##kurvaturMaks", &e, 0.005f, 0.001f, 5.0f, "%.4f 1/mm")) { a.kurvaturMaks = e; a.bayatAyarla(true); } if (ImGui::IsItemDeactivatedAfterEdit()) an.hesapla(a.kimlik()); } else if (a.tip() == AnalizTip::Sureklilik) { float e = a.g1EsikDeg; ui::alan("G1 Eşik"); if (ImGui::DragFloat("##g1Esik", &e, 0.5f, 0.1f, 90.0f, "%.1f °")) { a.g1EsikDeg = e; a.bayatAyarla(true); } if (ImGui::IsItemDeactivatedAfterEdit()) an.hesapla(a.kimlik()); } else if (a.tip() == AnalizTip::KeskinKenar) { float e = a.keskinEsikDeg; ui::alan("Keskinlik Eşiği"); if (ImGui::DragFloat("##keskinEsik", &e, 0.5f, 1.0f, 179.0f, "%.1f °")) { a.keskinEsikDeg = e; a.bayatAyarla(true); } if (ImGui::IsItemDeactivatedAfterEdit()) an.hesapla(a.kimlik()); if (ImGui::IsItemHovered()) ImGui::SetTooltip("İki komşu yüz arasındaki dihedral açı bu eşiğin üstündeyse\n" "kenar keskin sayılır ve sahnede renkli çizilir."); ImGui::TextColored(ImVec4(0.85f, 0.15f, 0.15f, 1.f), "▬ dışbükey"); ImGui::SameLine(); ImGui::TextColored(ImVec4(0.15f, 0.40f, 0.85f, 1.f), "▬ içbükey"); // ---- Pah/Fillet önerisi: analiz bulur → tek tıkla DM düzeltir ---- // Dışbükey → pah (C), içbükey → fillet (R). Çoklu kenar TEK OCCT feature + // TEK undo adımı (DuzenleIslem). Uygulama ordinalleri değiştirir → analiz // hemen yeniden hesaplanır (kalan keskin kenarlar yeni ordinallerle gelir). if (!a.keskinKenarlar.empty() && a.hedefGovde) { u32 dis = 0; for (const auto& k : a.keskinKenarlar) if (k.disbukey) ++dis; const u32 ic = (u32)a.keskinKenarlar.size() - dis; const auto oneriUygula = [&](bool yuvarlatMi, f64 deger) { std::vector kenarlar; kenarlar.reserve(a.keskinKenarlar.size()); for (const auto& k : a.keskinKenarlar) if (k.disbukey == !yuvarlatMi) kenarlar.push_back(k.kenarOrdinal); const DugumPusula eski = sahne.anlik(a.hedefGovde); if (sahne.pahYuvarlatUygulaKenarlar(a.hedefGovde, kenarlar, deger, yuvarlatMi)) { cekirdek.uygula( std::make_unique(sahne, eski, sahne.anlik(a.hedefGovde))); ui::toastGoster(yuvarlatMi ? "Fillet uygulandı" : "Pah uygulandı", ui::ToastTip::Basari); an.hesapla(a.kimlik()); // yeni ordinallerle tazele (görsel + rapor) } else { ui::toastGoster(yuvarlatMi ? "Fillet başarısız (yarıçapı küçültmeyi dene)" : "Pah başarısız (mesafeyi küçültmeyi dene)", ui::ToastTip::Hata); } }; ImGui::Separator(); ImGui::TextDisabled("Öneri"); char et[96]; if (dis) { std::snprintf(et, sizeof(et), "◺ %u dışbükeye C%.1f pah##oneriPah", dis, an.keskinOneriPah); if (ImGui::Button(et)) oneriUygula(false, (f64)an.keskinOneriPah); ImGui::SameLine(); ImGui::SetNextItemWidth(70); ImGui::DragFloat("##oneriPahBoy", &an.keskinOneriPah, 0.1f, 0.1f, 50.0f, "%.1f"); } if (ic) { std::snprintf(et, sizeof(et), "◜ %u içbükeye R%.1f fillet##oneriFillet", ic, an.keskinOneriFillet); if (ImGui::Button(et)) oneriUygula(true, (f64)an.keskinOneriFillet); ImGui::SameLine(); ImGui::SetNextItemWidth(70); ImGui::DragFloat("##oneriFilletR", &an.keskinOneriFillet, 0.1f, 0.1f, 50.0f, "%.1f"); } } } else if (a.tip() == AnalizTip::Zebra || a.tip() == AnalizTip::Reflect) { // CANLI-GPU: serit/faz slider'i shader uniform'una gider — yeniden hesap YOK. ui::alan("Şerit Sayısı"); ImGui::DragFloat("##zebraSerit", &a.zebraSerit, 0.5f, 1.0f, 128.0f, "%.0f"); ui::alan("Faz"); ImGui::DragFloat("##zebraAci", &a.zebraAci, 1.0f, -180.0f, 180.0f, "%.0f °"); } // Skaler-alan tipleri: renk rampası seçimi (canlı-GPU push-constant → anlık). if (a.tip() == AnalizTip::Radius || a.tip() == AnalizTip::Kalinlik || a.tip() == AnalizTip::Kurvatur) { const char* rampalar[] = {"Jet", "Turbo", "Tolerans"}; int ri = a.rampaTip == 0 ? 0 : (a.rampaTip == 3 ? 2 : 1); ui::alan("Renk Rampası"); if (ImGui::Combo("##renkRampasi", &ri, rampalar, 3)) a.rampaTip = (ri == 0) ? 0 : (ri == 2 ? 3 : 1); } sekmeBitir(); } // ── Sonuç: lejant/histogram + rapor tablosu ── if (sekme("Sonuç", "📊")) { // Sonuç lejantı + histogram. ImGui::TextColored(SOLUK, "Sonuç Lejantı"); lejantHistogram(a); // Rapor. ImGui::Separator(); ImGui::TextColored(SOLUK, "Rapor"); ImGui::TextWrapped("%s", a.ozet.c_str()); // Yükseklik satır sayısına göre (tavan ~10 satır): 90px sabitken numaralı yüz // dökümü kaydırma altında kalıyordu — kullanıcı listeyi hiç görmüyordu. const float rapYuk = ImGui::GetFrameHeight() * std::min(10.0f, (float)a.rapor.size() + 1.0f); if (!a.rapor.empty() && ImGui::BeginTable("rapor", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, {-1, rapYuk})) { ImGui::TableSetupColumn("Öğe"); ImGui::TableSetupColumn("Değer"); ImGui::TableHeadersRow(); for (int ri = 0; ri < (int)a.rapor.size(); ++ri) { auto& r = a.rapor[ri]; ImGui::TableNextRow(); ImGui::TableNextColumn(); // Ordinali olan satır TIKLANABİLİR: kamera o yüze uçar (sayı ↔ 3B bağı). if (r.ordinal) { ImGui::PushID(ri); ImGui::PushStyleColor(ImGuiCol_Text, r.kritik ? KIRMIZI : SOLUK); const bool tik = ImGui::Selectable(r.ad.c_str(), false, ImGuiSelectableFlags_SpanAllColumns); ImGui::PopStyleColor(); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Tıkla: kamera bu yüze gitsin"); ImGui::PopID(); if (tik) yuzeOdakla(sahne, an, a, r.ordinal); } else { ImGui::TextColored(r.kritik ? KIRMIZI : SOLUK, "%s", r.ad.c_str()); } ImGui::TableNextColumn(); ImGui::Text("%.2f %s", r.deger, a.sonuc.birim.c_str()); } ImGui::EndTable(); } sekmeBitir(); } sekmeKapat(); } // Alt eylem çubuğu. ImGui::Separator(); if (ImGui::Button(a.bayat() ? "⟳ Yeniden Hesapla" : "⚙ Hesapla", {-90, 0})) an.hesapla(a.kimlik()); ImGui::SameLine(); bool ak = a.aktif; if (ImGui::Checkbox("Renk", &ak)) { a.aktif = ak; an.keskinGorselGuncelle(a.kimlik()); // KeskinKenar: pasif → kenar düğümleri kalkar } ImGui::SameLine(); if (ImGui::Button("🗑", {-1, 0})) { const u32 kk = a.kimlik(); an.sil(kk); g_anAcik[kk] = false; erken = true; } } // if (pen) // Kapanınca renk override'ı KALDIR (model eskiye dönsün) — aktif=false. if (!erken && !acik) { g_anAcik[a.kimlik()] = false; a.aktif = false; an.keskinGorselGuncelle(a.kimlik()); } } // ========================= TEK KESİT PENCERESİ ============================ void kesitPenceresiCiz(Cekirdek& cekirdek, Sahne& sahne, AnalizSistemi& an, KesitDuzlemi& k) { (void)cekirdek; const ImVec4 SOLUK = ui::tema().metinSoluk; bool& ustte = g_ksPin[k.kimlik()]; char baslik[128]; std::snprintf(baslik, sizeof(baslik), "✂ %s###kesit_%u", k.ad().c_str(), k.kimlik()); using namespace fiko::ui; using u32 = fiko::u32; // ui::u32(ImVec4) ile tip cakismasini onler bool acik = true, erken = false; ui::Pencere pen{baslik, &acik, {.boyut = {300, 0}, .pinDurum = &ustte, .skin = ui::PencereSkin::WebCam}}; if (pen) { bool degisti = false; ImGui::TextColored(SOLUK, "Düzlem Normali"); if (ImGui::Button("X")) { k.normal = {1, 0, 0}; degisti = true; } ImGui::SameLine(); if (ImGui::Button("Y")) { k.normal = {0, 1, 0}; degisti = true; } ImGui::SameLine(); if (ImGui::Button("Z")) { k.normal = {0, 0, 1}; degisti = true; } ImGui::SameLine(); if (ImGui::Button("-X")) { k.normal = {-1, 0, 0}; degisti = true; } ImGui::SameLine(); if (ImGui::Button("-Y")) { k.normal = {0, -1, 0}; degisti = true; } ImGui::SameLine(); if (ImGui::Button("-Z")) { k.normal = {0, 0, -1}; degisti = true; } { const bool aktif = an.secimAktif() && an.secimKip() == analiz::SecimKip::KesitDuzlem; if (aktif) ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{0.08f, 0.42f, 0.55f, 1.0f}); if (ImGui::Button("🎯 Yüzeyden", {-1, 0})) an.kesitSecimBaslat(k.kimlik(), [] {}); if (aktif) ImGui::PopStyleColor(); } ImGui::Text("N: %.2f %.2f %.2f", k.normal.x, k.normal.y, k.normal.z); float mesafe = k.mesafe; ui::alan("Mesafe"); if (ImGui::DragFloat("##mesafe", &mesafe, 0.2f, -1e4f, 1e4f, "%.2f mm")) { k.mesafe = mesafe; degisti = true; } bool aktif = k.aktif, ters = k.ters, profil = k.profilGoster; if (ImGui::Checkbox("Kesit Aktif (clip)", &aktif)) { k.aktif = aktif; degisti = true; } ImGui::SameLine(); if (ImGui::Checkbox("Ters", &ters)) { k.ters = ters; degisti = true; } if (ui::anahtarSatiri("Profil Göster", &profil)) { k.profilGoster = profil; degisti = true; } kapakKipiCombo(an); if (degisti) an.kesitGuncelle(k.kimlik()); // DXF / SVG export. ImGui::Separator(); ImGui::TextColored(SOLUK, "Dışa Aktar (kesit konturu)"); const auto exportEt = [&](bool svg) { const auto dunya = kesitDunyaEgriler(sahne, k); Vec3f u, v; duzlemBaz(k.normal, u, v); const Vec3f o = k.nokta(); std::vector> uv; for (auto& poly : dunya) { std::vector pr; for (auto& p : poly) { const Vec3f w{(f32)p.x - o.x, (f32)p.y - o.y, (f32)p.z - o.z}; pr.push_back({(f64)w.nokta(u), (f64)w.nokta(v)}); } if (pr.size() >= 2) uv.push_back(std::move(pr)); } if (uv.empty()) return; const std::string yol = sistem::calistirilabilirDizin() + (svg ? "kesit.svg" : "kesit.dxf"); (void)(svg ? svgYaz(yol, uv) : dxfYaz(yol, uv)); }; if (ImGui::Button("DXF ↓", {-90, 0})) exportEt(false); ImGui::SameLine(); if (ImGui::Button("SVG ↓", {-1, 0})) exportEt(true); ImGui::Separator(); if (ImGui::Button("🗑 Sil", {-1, 0})) { const u32 kk = k.kimlik(); an.kesitSil(kk); g_ksAcik[kk] = false; erken = true; } } // if (pen) if (!erken && !acik) g_ksAcik[k.kimlik()] = false; } // ========================= KESİT LİSTESİ PENCERESİ ======================== // Web çoklu-kesit paneli paritesi: tüm düzlemler tek tabloda — ekle/sil/ // aç-kapa/ters/eksen combo/ofset drag + global kapak kipi. void kesitListePenceresiCiz(Sahne& sahne, AnalizSistemi& an) { if (!g_ksListe) return; (void)sahne; using namespace fiko::ui; using u32 = fiko::u32; // ui::u32(ImVec4) ile tip cakismasini onler bool acik = true; ui::Pencere pen{"✂ Kesit Düzlemleri###kesit_liste", &acik, {.boyut = {430, 300}, .pinDurum = &g_ksListePin, .skin = ui::PencereSkin::WebCam}}; if (pen) { if (ImGui::Button("➕ Yeni Kesit")) { const u32 k = an.kesitEkle(0); if (k) g_ksAcik[k] = true; } ImGui::SameLine(); kapakKipiCombo(an); if (an.kesitler().size() > 4) ui::bilgiSeridi(2, "İlk 4 aktif düzlem GPU clip'e girer"); if (ImGui::BeginTable("kesitler", 6, ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingStretchProp, {-1, -1})) { ImGui::TableSetupColumn("Aktif", ImGuiTableColumnFlags_WidthFixed, 38.0f); ImGui::TableSetupColumn("Ad"); ImGui::TableSetupColumn("Eksen", ImGuiTableColumnFlags_WidthFixed, 74.0f); ImGui::TableSetupColumn("Ofset (mm)"); ImGui::TableSetupColumn("Ters", ImGuiTableColumnFlags_WidthFixed, 34.0f); ImGui::TableSetupColumn("##sil", ImGuiTableColumnFlags_WidthFixed, 30.0f); ImGui::TableHeadersRow(); u32 silinecek = 0; for (u32 kk : an.kesitler()) { KesitDuzlemi* k = an.kesitBul(kk); if (!k) continue; ImGui::PushID((int)kk); ImGui::TableNextRow(); bool degisti = false; ImGui::TableNextColumn(); bool aktif = k->aktif; if (ImGui::Checkbox("##aktif", &aktif)) { k->aktif = aktif; degisti = true; } ImGui::TableNextColumn(); if (ImGui::Selectable(k->ad().c_str(), g_ksAcik.count(kk) && g_ksAcik[kk])) g_ksAcik[kk] = true; // satıra tık → ayrıntı penceresi if (ImGui::IsItemHovered()) ImGui::SetTooltip("Tıkla: ayrıntı penceresini aç"); ImGui::TableNextColumn(); const char* eksenler[] = {"X", "Y", "Z", "-X", "-Y", "-Z", "Serbest"}; int ei = eksenIndeks(k->normal); ImGui::SetNextItemWidth(-1); if (ImGui::Combo("##eksen", &ei, eksenler, 7) && ei < 6) { k->normal = eksenNormal(ei); degisti = true; } ImGui::TableNextColumn(); float mesafe = k->mesafe; ImGui::SetNextItemWidth(-1); if (ImGui::DragFloat("##ofset", &mesafe, 0.2f, -1e4f, 1e4f, "%.2f")) { k->mesafe = mesafe; degisti = true; } ImGui::TableNextColumn(); bool ters = k->ters; if (ImGui::Checkbox("##ters", &ters)) { k->ters = ters; degisti = true; } ImGui::TableNextColumn(); if (ImGui::Button("🗑")) silinecek = kk; if (degisti) an.kesitGuncelle(kk); ImGui::PopID(); } if (silinecek) { an.kesitSil(silinecek); g_ksAcik.erase(silinecek); } ImGui::EndTable(); } } // if (pen) if (!acik) g_ksListe = false; } } // namespace void analizPencereAc(u32 kimlik) { g_anAcik[kimlik] = true; } void analizPencereleriCiz(Cekirdek& cekirdek, Sahne& sahne, AnalizSistemi& an) { for (auto it = g_anAcik.begin(); it != g_anAcik.end();) { if (!it->second) { it = g_anAcik.erase(it); continue; } AnalizNesnesi* a = an.bul(it->first); if (!a) { it = g_anAcik.erase(it); continue; } // CAD Fark kendi özel paneline yönlenir (web CadDiff birebir düzeni). if (a->tip() == AnalizTip::Fark) farkPanelCiz(sahne, an, *a, it->second); else analizPenceresiCiz(cekirdek, sahne, an, *a); ++it; } // Yan-yana 3D karşılaştırma penceresi (web dialog karşılığı; panelden açılır). farkKarsilastirmaCiz(sahne, an); } void kesitPencereAc(u32 kimlik) { g_ksAcik[kimlik] = true; } bool kesitPencereAcikMi(u32 kimlik) { const auto it = g_ksAcik.find(kimlik); return (it != g_ksAcik.end() && it->second) || g_ksListe; } void kesitListePencereAc() { g_ksListe = true; } void kesitPencereleriCiz(Cekirdek& cekirdek, Sahne& sahne, AnalizSistemi& an) { kesitListePenceresiCiz(sahne, an); for (auto it = g_ksAcik.begin(); it != g_ksAcik.end();) { if (!it->second) { it = g_ksAcik.erase(it); continue; } KesitDuzlemi* k = an.kesitBul(it->first); if (!k) { it = g_ksAcik.erase(it); continue; } kesitPenceresiCiz(cekirdek, sahne, an, *k); ++it; } } } // namespace fiko