#include "hizli3d_penceresi.hpp" #include #include #include #include #include #include #include "fiko/cekirdek/dunya.hpp" #include "fiko/render/orbit_kamera.hpp" #include "fiko/sahne/kutuk_hesap.hpp" // kutukAgirlikKg #include "fiko/sahne/sahne.hpp" #include "fiko/ui/animasyon.hpp" // yay / animSifirla / nabiz / zaman #include "fiko/ui/bilesen.hpp" // glowCerceve / hudKoseleri / yayMetin #include "fiko/ui/efekt.hpp" // Efektler::aurora #include "fiko/ui/sektor.hpp" // sektorCiz (halka dilim primitifi) #include "fiko/ui/tema.hpp" // tema() token + u32 / karisim #include "imgui.h" #include "imgui_internal.h" #include "ui_ortak.hpp" // tipAdi / tipSimge #include "fiko/malzeme/malzeme.hpp" #include "uygulama_baglam.hpp" namespace fiko { using ui::glowCerceve; using ui::karisim; using ui::sektorCiz; using ui::tema; // NOT: ui::u32, fiko::u32 (uint32_t) tip adıyla çakışır → nitelikli çağrılır. namespace { constexpr float PI = 3.14159265f; constexpr float CAP = 290.0f; // kare pencere kenarı (küçültüldü: 360→290) constexpr float GLOW_PAY = 14.0f; // daire dışındaki glow/aurora taşma marjı (kırpılmasın) // Dünya (dış) uzayda ekran-ortalı metin — DrawList ile serbest yerleşim. void ortaliText(ImDrawList* dl, ImVec2 mrk, float fs, ImU32 renk, const char* s) { ImFont* f = ImGui::GetFont(); const ImVec2 mb = f->CalcTextSizeA(fs, FLT_MAX, 0.0f, s); dl->AddText(f, fs, {mrk.x - mb.x * 0.5f, mrk.y - mb.y * 0.5f}, renk, s); } // Bir ImGui öğesini (buton vb.) verilen ekran noktasına MERKEZLENECEK şekilde // konumlandırmak için imleci ayarlar (radyal dizilim: her buton bir yay noktasında). void imlecMerkez(ImVec2 nokta, float genislik, float yukseklik) { ImGui::SetCursorScreenPos({nokta.x - genislik * 0.5f, nokta.y - yukseklik * 0.5f}); } // Kompakt tek-satır ipucu. KRİTİK: widget aktif WindowRounding=CAP*0.5 (~145) push // ediyor; sıfırlanmazsa tooltip bu dev rounding'i miras alıp ImGui'yi ~2*rounding // minimum boyuta zorlar (kocaman kutu). Rounding+padding'i tooltip için SABİTLE. void ipucu(const char* s) { ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{8, 4}); if (ImGui::BeginTooltip()) { ImGui::TextUnformatted(s); ImGui::EndTooltip(); } ImGui::PopStyleVar(2); } // Eksen/EksenSistemi düğümüne özel Hızlı 3D widget (registry'den çağrılır). Aşağıda tanımlı. bool eksenHizliWidgetCiz(UygulamaBaglam& b); // Tip-özel Hızlı 3D widget kayıt defteri — web dinamik-bileşen registry'sinin native // karşılığı: kayıtlı çizer o tipe özel widget çizip true dönerse standart ağırlık/kütük // widget'ı atlanır; kayıt yoksa standarda düşülür. Yeni tip eklemek = buraya bir satır. using HizliCizer = bool (*)(UygulamaBaglam&); const std::unordered_map& hizliWidgetKayit() { static const std::unordered_map m = { {DugumTip::Eksen, eksenHizliWidgetCiz}, {DugumTip::EksenSistemi, eksenHizliWidgetCiz}, }; return m; } } // namespace void hizli3dPencereCiz(UygulamaBaglam& b) { HizliUIDurum& h = b.hizli; if (!h.hizli3dAcik) return; Sahne& sahne = b.sahne; OrbitKamera& kamera = b.kamera; Dugum* hd = sahne.bul(h.hizli3dKimlik); if (!hd) { h.hizli3dAcik = false; return; } // TİP-ÖZEL WIDGET (dinamik-bileşen registry deseni): kayıtlı çizer varsa o tipe özel // widget çizilir; yoksa aşağıdaki standart ağırlık/kütük widget'ına düşülür. if (auto it = hizliWidgetKayit().find(hd->tip); it != hizliWidgetKayit().end() && it->second(b)) return; // GUARD (B): Ağırlık/kütük yalnız GERÇEK KATI/mesh için anlamlı. Datum // (Eksen/Düzlem/EksenSistemi) ve wireframe (Tel/TakımYolu/Sketch) mesh'siz // (indeksSayi==0) → geçersiz sınır/sıfır-boyut → yayMetin/kütük NaN riski + AV. // Bu düğümlerde widget'i AÇMA (main.cpp da açmaz; bu savunma katmanı). Stack // henüz push'lanmadı → dengeli çıkış. if (hd->indeksSayi == 0) { h.hizli3dAcik = false; return; } // Ölçüler kare başında ÇİZİLEN kutuyla aynı kaynaktan (hizliBoyut) — DEĞİŞMEZ. const Vec3f olcek{(f32)hd->donusum.olcek.x, (f32)hd->donusum.olcek.y, (f32)hd->donusum.olcek.z}; const f64 hacimG = hd->hacim * (f64)olcek.x * olcek.y * olcek.z; // ATANMIŞ MALZEME KAZANIR: gövdeye malzeme atanmışsa ağırlık onun yoğunluğundan // hesaplanır (elle özkütle ayarı yalnız malzemesiz gövdede geçerli) — tek doğruluk // kaynağı. kg/m³ → g/cm³ = /1000. const malzeme::MalzemeTanim* atanmisMalzeme = b.malzemeKut.bul(b.malzemeAta.govdeninMalzemesi(hd->kimlik)); const f32 ozkutle = atanmisMalzeme ? (f32)(atanmisMalzeme->yogunluk / 1000.0) : h.hizliOzkutle; const f64 agirlik = kutukAgirlikKg(hacimG, ozkutle); const ui::Tema& t = tema(); const ImVec4 neon = t.vurgu; // cyan (birincil) const ImVec4 altin = t.uyari; // altın/sarı (aksan) const ImVec4 eksenRenk[3] = { {0.95f, 0.35f, 0.38f, 1.0f}, {0.40f, 0.92f, 0.50f, 1.0f}, {0.35f, 0.60f, 1.0f, 1.0f}}; // Giriş animasyonu (yay/scale-in): açıldığı/nesne değiştiği kare 0→1 yaylanır. const ImGuiID animId = ImHashStr("hizli3dGiris"); static u32 sonKimlik = 0; if (sonKimlik != h.hizli3dKimlik) { ui::animSifirla(animId, 0.0f); sonKimlik = h.hizli3dKimlik; h.hizliAyarAcik = false; // yeni nesnede ayar alanı kapalı başlar } const float giris = ui::yay(animId, 1.0f, 240.0f, 22.0f); const float gaC = ImClamp(giris, 0.0f, 1.0f); // alfa çarpanı için sınırlı // Şeffaf zemin — daire DrawList'ten gelir, WindowBg TAM ŞEFFAF. ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4{0, 0, 0, 0}); ImGui::PushStyleColor(ImGuiCol_TitleBg, ImVec4{0, 0, 0, 0}); ImGui::PushStyleColor(ImGuiCol_TitleBgActive, ImVec4{0, 0, 0, 0}); ImGui::PushStyleColor(ImGuiCol_Border, karisim(t.vurgu, t.vurgu2, 0.35f)); ImGui::PushStyleColor(ImGuiCol_FrameBg, karisim(t.cerceve, t.arka, 0.35f)); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, karisim(t.cerceve, t.vurgu, 0.18f)); ImGui::PushStyleColor(ImGuiCol_Button, karisim(t.panel, t.arka, 0.25f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, karisim(t.panel, t.vurgu, 0.30f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, karisim(t.panel, t.vurgu, 0.50f)); ImGui::PushStyleColor(ImGuiCol_Text, t.metin); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 6.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, CAP * 0.5f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{0, 0}); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2{5, 3}); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{5, 2}); // ANA VIEWPORT'a kilitle: ayrı OS penceresi doğmaz → widget ana uygulama // penceresinin dışına OS-seviyesinde ÇIKAMAZ (multi-viewport bu pencerede kapalı). const ImGuiViewport* anaVp = ImGui::GetMainViewport(); ImGui::SetNextWindowViewport(anaVp->ID); // MOUSE-MERKEZLİ AÇILIŞ: main.cpp açılışta hizli3dAcilisX/Y'ye mouse ekran konumunu // yazar. YALNIZ açılış karesinde (acilis noktası değişince) pencereyi mouse MERKEZ // olacak konuma taşı + ana-viewport work-area'ya clamp'le. Sonraki karelerde // konumlama YAPILMAZ → kullanıcı serbest taşıyabilir (her-kare clamp aşağıda korur). { const ImVec2 amin = anaVp->WorkPos; const ImVec2 amax{anaVp->WorkPos.x + anaVp->WorkSize.x, anaVp->WorkPos.y + anaVp->WorkSize.y}; static f32 sonAcX = 1e30f, sonAcY = 1e30f; const bool yeniAcilis = std::fabs(h.hizli3dAcilisX - sonAcX) > 0.5f || std::fabs(h.hizli3dAcilisY - sonAcY) > 0.5f; if (yeniAcilis && (h.hizli3dAcilisX != 0 || h.hizli3dAcilisY != 0)) { sonAcX = h.hizli3dAcilisX; sonAcY = h.hizli3dAcilisY; ImVec2 p{h.hizli3dAcilisX - CAP * 0.5f, h.hizli3dAcilisY - CAP * 0.5f}; // mouse merkez p.x = std::clamp(p.x, amin.x, std::max(amin.x, amax.x - CAP)); p.y = std::clamp(p.y, amin.y, std::max(amin.y, amax.y - CAP)); ImGui::SetNextWindowPos(p, ImGuiCond_Always); } else { ImGui::SetNextWindowPos({b.vpRectMinX + 70, b.vpRectMinY + 70}, ImGuiCond_FirstUseEver); } } ImGui::SetNextWindowSize({CAP, CAP}, ImGuiCond_Always); ImGui::SetNextWindowBgAlpha(0.0f); // TAŞINABİLİR (NoMove YOK) ama ana viewport work-area'ya HER KARE CLAMP'li → sürükler- // ken de kenarda durur, dışarı çıkamaz/kaybolmaz. Çizim clamp'lenmiş konuma göre yapılır. const bool acik = ImGui::Begin("###hizli3d", &h.hizli3dAcik, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoFocusOnAppearing); ImGuiWindow* w = ImGui::GetCurrentWindow(); // HEP ÜSTTE (z-sıra) — AMA tooltip aktifken öne getirme: tooltip ayrı bir ImGui // penceresidir; widget'i her kare öne almak tooltip'i ARKADA bırakıp gizlerdi. // Bir önceki kare bir öğe hover'daysa (tooltip gösteriliyor) öne-almayı atla. // (IsAnyItemHovered bu noktada henüz bu karenin öğelerini görmez → önceki kare izlenir.) static bool oncekiHover = false; if (!oncekiHover) ImGui::BringWindowToDisplayFront(w); const ImVec2 ws = ImGui::GetWindowSize(); // HER KARE CLAMP (madde 5 kök neden fix): eski kod clamp'i Begin SONRASI geç // uyguluyordu → o kare dragged konumda kırpılıp kayboluyordu. DOĞRU: konumu şimdi // clamp'le, sapma varsa SetWindowPos, VE mrk'yi clamp'li konumdan türet → daire // aktif sürükleme sırasında bile clamp'li yerde TAM çizilir, kenarı geçemez. ImVec2 wp = ImGui::GetWindowPos(); { const ImVec2 amin = anaVp->WorkPos; const ImVec2 amax{anaVp->WorkPos.x + anaVp->WorkSize.x, anaVp->WorkPos.y + anaVp->WorkSize.y}; const ImVec2 c{std::clamp(wp.x, amin.x, std::max(amin.x, amax.x - ws.x)), std::clamp(wp.y, amin.y, std::max(amin.y, amax.y - ws.y))}; if (std::fabs(c.x - wp.x) > 0.5f || std::fabs(c.y - wp.y) > 0.5f) { ImGui::SetWindowPos(c); wp = c; // bu karenin çizimi de clamp'li konumu kullanır (geç-düzeltme yok) } } const ImVec2 mrk{wp.x + ws.x * 0.5f, wp.y + ws.y * 0.5f}; // daire merkezi (clamp'li) // Yarıçap: pencereye GLOW_PAY marjı bırakılır → dış glow halkası kırpılmaz. const float Rtam = std::min(ws.x, ws.y) * 0.5f - GLOW_PAY; const float R = Rtam * (0.92f + 0.08f * gaC); // pop: hafif büyür ImDrawList* dl = ImGui::GetWindowDrawList(); // Dejenere kare (0-boyut / gizli / minimize): çizme, aksi halde negatif yarıçap // primitifleri + boş içerik alanı ImGui'yi bozabilir. End() DENGELİ kalır. if (R < 1.0f) { ImGui::End(); ImGui::PopStyleVar(6); ImGui::PopStyleColor(10); return; } // ---- TAM DAİRE zemin (arka DrawList, içeriğin ALTINA) ---- dl->AddCircleFilled(mrk, R, ui::u32(t.arka, gaC), 64); dl->PushClipRect({mrk.x - R, mrk.y - R}, {mrk.x + R, mrk.y + R}, true); ui::Efektler::aurora(dl, {mrk.x - R, mrk.y - R}, {mrk.x + R, mrk.y + R}, (t.koyu ? 0.32f : 0.14f) * gaC); dl->PopClipRect(); dl->AddCircleFilled(mrk, R, ui::u32(t.arka, 0.32f * gaC), 64); dl->AddCircleFilled(mrk, R * 0.62f, ui::u32(t.panel, 0.22f * gaC), 64); for (int i = 3; i >= 1; --i) dl->AddCircle(mrk, R + (float)i * 2.0f, ui::u32(t.vurgu, 0.14f * gaC * t.glowSiddet / (float)i), 64, 2.5f); dl->AddCircle(mrk, R, ui::u32(karisim(t.vurgu, t.vurgu2, 0.4f), 0.9f * gaC), 64, 1.8f); dl->AddCircle(mrk, R * 0.66f, ui::u32(t.vurgu, 0.22f * gaC), 64, 1.0f); { const float f0 = ui::zaman() * 0.6f; dl->PathArcTo(mrk, R - 2.0f, f0, f0 + 0.5f, 24); dl->PathStroke(ui::u32({1, 1, 1, 0.30f * gaC}), 0, 2.0f); } if (acik) { // ---- Dairesel içerik: ÜST = ad/hacim/özkütle (yay-yazı), ORTA-ÜST = AĞIRLIK, // ALT = XYZ ölçü sektörleri + radyal kontrol butonları. ---- // Nesne adı (en tepe yay, dışa bakar) { char ad[96]; std::snprintf(ad, sizeof(ad), "%s %s", tipSimge(hd->tip), hd->ad.empty() ? tipAdi(hd->tip) : hd->ad.c_str()); ui::yayMetin(dl, mrk, R * 0.90f, -PI * 0.5f, ad, ui::u32(karisim(t.vurgu, t.metin, 0.35f), gaC), true, 12.0f); } // Hacim (sol-üst) + özkütle (sağ-üst) yay-yazı if (hd->hacim > 0) { char hv[40]; std::snprintf(hv, sizeof(hv), "%.1f cm³", hd->hacim / 1000.0); ui::yayMetin(dl, mrk, R * 0.90f, -PI * 0.82f, hv, ui::u32(t.metinSoluk, gaC), true, 16.0f); } { char ok[40]; std::snprintf(ok, sizeof(ok), "%.2f g/cm³", ozkutle); ui::yayMetin(dl, mrk, R * 0.90f, -PI * 0.18f, ok, ui::u32(t.metinSoluk, gaC), true, 16.0f); } // MERKEZ: ayarlar KAPALIYSA büyük AĞIRLIK; AÇIKSA merkez-bağlı ayar alanı (aşağıda). const float agY = mrk.y - R * 0.22f; // ağırlık bloğu merkezi if (!h.hizliAyarAcik) { ortaliText(dl, {mrk.x, agY - 22.0f}, 11.0f, ui::u32(altin, gaC), atanmisMalzeme ? atanmisMalzeme->ad.c_str() : "AĞIRLIK"); char agirlikMetin[48]; std::snprintf(agirlikMetin, sizeof(agirlikMetin), "%.3f", agirlik); const f32 nb = 0.85f + 0.15f * ui::nabiz(0.6f); ortaliText(dl, {mrk.x, agY}, 34.0f, ui::u32(karisim(t.metin, neon, nb), gaC), agirlikMetin); ortaliText(dl, {mrk.x, agY + 22.0f}, 14.0f, ui::u32(karisim(neon, t.metinSoluk, 0.4f), gaC), "kg"); } else { // ---- MERKEZ-BAĞLI AYARLAR (⚙ ile açılır): özkütle + saydam + sıfırla. ---- // Ağırlık gizli → çakışma yok; ayarlar dairenin ORTA bandında, merkeze bağlı. ortaliText(dl, {mrk.x, agY - 30.0f}, 11.0f, ui::u32(altin, gaC), "⚙ AYARLAR"); float ay = agY - 18.0f; const auto aSatir = [&](float gen) { ImGui::SetCursorScreenPos({mrk.x - gen * 0.5f, ay}); }; const auto aBit = [&]() { ay = ImGui::GetItemRectMax().y + 3.0f; }; // Özkütle girdi + sıfırla aSatir(150.0f); ImGui::SetNextItemWidth(120.0f); ImGui::BeginDisabled(atanmisMalzeme != nullptr); // malzeme yoğunluğu kazanır ImGui::InputFloat("##ozkAyar", &h.hizliOzkutle, 0.0f, 0.0f, "%.2f g/cm³"); ImGui::EndDisabled(); if (atanmisMalzeme && ImGui::IsItemHovered()) ipucu("Yoğunluk atanmış malzemeden geliyor (Malzeme penceresinden değiştir)"); if (h.hizliOzkutle < 0.01f) h.hizliOzkutle = 0.01f; ImGui::SameLine(0, 4.0f); if (ImGui::SmallButton("↻##ozkA")) h.hizliOzkutle = 2.71f; if (ImGui::IsItemHovered()) ipucu("Özkütleyi sıfırla (2.71)"); aBit(); // Kütük saydamlığı aSatir(150.0f); ImGui::SetNextItemWidth(150.0f); ImGui::SliderFloat("##saydamAyar", &h.hizliKutukSaydam, 0.0f, 0.6f, "Saydam %.2f"); aBit(); // Tümünü sıfırla aSatir(110.0f); if (ImGui::Button("↻ Sıfırla", {110, 0})) { h.hizliKutukMod = 0; h.hizliOzkutle = 2.71f; h.hizliKutukSaydam = 0.15f; h.hizliYonSecim = 0; h.hizliYon1Var = h.hizliYon2Var = false; } } // ALT DIŞ YAY: kütük ölçü sektörleri, SOL→SAĞ = X, Y, Z = BÜYÜK→ORTA→KÜÇÜK. // Boyut büyükten küçüğe sıralanır (kutukSirala): en büyük→X(kırmızı,sol), // orta→Y(yeşil,orta), en küçük→Z(mavi,sağ). Renkler etikete bağlı. const Vec3f bsirali = kutukSirala(h.hizliBoyut); // x=büyük, y=orta, z=küçük const char* eksenAd[3] = {"X", "Y", "Z"}; const f32 eksenDeger[3] = {bsirali.x, bsirali.y, bsirali.z}; const float rOlcu = R * 0.80f; // Ekran +y-aşağı: 0.16π=SAĞ, 0.84π=SOL. Sol→sağ X,Y,Z için eksen a'nın açı bandı // bi = 2-a (a=0/X → en sol bant, a=2/Z → en sağ bant). const float ea0 = PI * 0.16f, ea1 = PI * 0.84f; for (int a = 0; a < 3; ++a) { const int bi = 2 - a; // X→sol, Z→sağ const float aBas = ea0 + (ea1 - ea0) * (float)bi / 3.0f + 0.03f; const float aBit = ea0 + (ea1 - ea0) * (float)(bi + 1) / 3.0f - 0.03f; sektorCiz(dl, mrk, R * 0.70f, R * 0.94f, aBas, aBit, ui::u32(karisim(t.panel, eksenRenk[a], 0.16f), 0.6f * gaC), ui::u32(eksenRenk[a], 0.55f * gaC), 1.0f); const float aOrta = (aBas + aBit) * 0.5f; // Dairesel iki katman: eksen HARFİ ÜSTTE (dış yarıçap, küçük), SAYI ALTTA (iç // yarıçap, BÜYÜK ve okunur). Yarıçaplar birbirine YAKIN → tek grup gibi durur // (üst üste binmez). Her ikisi yayMetin ile dairesel, içe okunur (ustYay=false). ui::yayMetin(dl, mrk, R * 0.88f, aOrta, eksenAd[a], ui::u32(eksenRenk[a], gaC), false, 11.0f); char dm[24]; std::snprintf(dm, sizeof(dm), "%.3f", eksenDeger[a]); ui::yayMetin(dl, mrk, R * 0.77f, aOrta, dm, ui::u32(t.metin, gaC), false, 15.0f); } // ---- ÜST DAİRESEL AKSİYON İKON YAYI: Seç/Odakla/Gizle/Sahne/Ayarlar ---- // Hepsi üst yayda (-0.80π..-0.20π, sol-üst→sağ-üst), eşit açısal aralıkla, R*0.62'de. // Ağırlık metninin ÜSTÜNDE bir bant → çakışmaz. Alt yarı = ölçü+mod (ayrık). constexpr int AK = 5; const float rBtn = R * 0.62f; const char* ikonlar[AK] = {"⊹", "⛶", hd->gorunur ? "◉" : "○", h.hizliKutukSahne ? "◈" : "◇", "⚙"}; const char* ipuclari[AK] = {"Seç", "Odakla", hd->gorunur ? "Gizle" : "Göster", h.hizliKutukSahne ? "Kütüğü gizle" : "Kütüğü sahnede göster", "Ayarlar"}; const float ika0 = -PI * 0.72f, ika1 = -PI * 0.28f; // hacim/özkütle yay-yazısından içeride for (int i = 0; i < AK; ++i) { const float a = ika0 + (ika1 - ika0) * (float)i / (float)(AK - 1); const ImVec2 p{mrk.x + std::cos(a) * rBtn, mrk.y + std::sin(a) * rBtn}; imlecMerkez(p, 24.0f, 22.0f); ImGui::PushID(i); if (i == 3) ImGui::PushStyleColor(ImGuiCol_Text, h.hizliKutukSahne ? t.basari : t.metinSoluk); const bool tik = ImGui::Button(ikonlar[i], {24, 22}); if (i == 3) ImGui::PopStyleColor(); if (ImGui::IsItemHovered()) ipucu(ipuclari[i]); if (tik) { if (i == 0) sahne.sec({h.hizli3dKimlik, 0}, false); else if (i == 1) kamera.sigdir(hd->dunyaMerkez(), hd->dunyaYaricap() + 0.4f); else if (i == 2) b.gorunurDegistir(h.hizli3dKimlik, !hd->gorunur); else if (i == 3) h.hizliKutukSahne = !h.hizliKutukSahne; else h.hizliAyarAcik = !h.hizliAyarAcik; // merkez-bağlı ayarlar aç/kapa } ImGui::PopID(); } // ---- Merkez-alt kompakt kontroller: mod segmenti (+ Özel modda yön seçimi) ---- // Ayarlar AÇIKKEN gizlenir (merkez ayar alanıyla çakışmasın; ⚙ ile geri gelir). if (!h.hizliAyarAcik) { float sY = mrk.y + R * 0.04f; // mod segmenti başlangıcı ("kg" altında, biraz yukarı) const auto satir = [&](float genislik) { ImGui::SetCursorScreenPos({mrk.x - genislik * 0.5f, sY}); }; const auto satirBitti = [&]() { sY = ImGui::GetItemRectMax().y + 4.0f; }; // Kütük yönü modu segmenti: Eksen | OBB | Özel const char* modAd[3] = {"Eksen", "OBB", "Özel"}; const float mw = 44.0f; satir(mw * 3.0f + 8.0f); for (int m = 0; m < 3; ++m) { const bool etkin = h.hizliKutukMod == m; if (etkin) ImGui::PushStyleColor(ImGuiCol_Button, karisim(t.panel, t.vurgu, 0.55f)); if (ImGui::Button(modAd[m], {mw, 0})) { h.hizliKutukMod = m; if (m != 2) h.hizliYonSecim = 0; } if (etkin) ImGui::PopStyleColor(); if (m < 2) ImGui::SameLine(); } satirBitti(); // Özel yön seçim (yalnız mod==Özel) — kendi satırında, kompakt if (h.hizliKutukMod == 2) { satir(148.0f); if (ImGui::SmallButton(h.hizliYonSecim == 1 ? "1.✎" : "1.Yön")) h.hizliYonSecim = (h.hizliYonSecim == 1) ? 0 : 1; ImGui::SameLine(); ImGui::TextColored(h.hizliYon1Var ? t.basari : t.metinSoluk, h.hizliYon1Var ? "✓" : "-"); ImGui::SameLine(); ImGui::BeginDisabled(!h.hizliYon1Var); if (ImGui::SmallButton(h.hizliYonSecim == 2 ? "2.✎" : "2.Yön")) h.hizliYonSecim = (h.hizliYonSecim == 2) ? 0 : 2; ImGui::EndDisabled(); ImGui::SameLine(); ImGui::TextColored(h.hizliYon2Var ? t.basari : t.metinSoluk, h.hizliYon2Var ? "✓" : "-"); if (h.hizliYon2Var) { ImGui::SameLine(); if (ImGui::SmallButton("✕")) h.hizliYon2Var = false; } satirBitti(); } } // !hizliAyarAcik (mod segmenti) } // ---- BOŞA TIKLA-KAPAT (popup davranışı) + ESC ---- // Daire DIŞINA sol/sağ tık → kapat. Daire İÇİ tıklar kontrollere gider. // İLK KARE ATLA: açılışı yapan uzun-bas bırakma tıkı (widget dışında olur) // widget'i hemen kapatmasın (aksi halde açılır-açılmaz kapanır). { static u32 acilisKimlik = 0; const bool ilkKare = acilisKimlik != h.hizli3dKimlik; acilisKimlik = h.hizli3dKimlik; // SEÇİM MODU: Özel modda yön/yüz seçimi beklenirken (hizliYonSecim>0) kullanıcı // sahneye (daire DIŞINA) tıklar → bu tık SEÇİM içindir, kapatma DEĞİL. Boşa-tıkla // bastırılır; ESC ise seçimi iptal eder (widget'i kapatmaz). // ÖNCEKİ KARE de izlenir: main.cpp seçim tıkını BU widget çiziminden ÖNCE tüketip // hizliYonSecim=0 yapar → o pick tıkının widget'i kapatmasını da bastırmak gerek. static u32 onKimlik = 0; static bool onSecimBekleniyor = false; if (onKimlik != h.hizli3dKimlik) onSecimBekleniyor = false; // yeni nesnede sıfırla const bool secimBekleniyor = h.hizliYonSecim > 0 || onSecimBekleniyor; onKimlik = h.hizli3dKimlik; onSecimBekleniyor = h.hizliYonSecim > 0; const ImVec2 f = ImGui::GetMousePos(); const float dx = f.x - mrk.x, dy = f.y - mrk.y; const bool disari = (dx * dx + dy * dy) > R * R; const bool tik = ImGui::IsMouseClicked(ImGuiMouseButton_Left) || ImGui::IsMouseClicked(ImGuiMouseButton_Right); if (ImGui::IsKeyPressed(ImGuiKey_Escape)) { if (secimBekleniyor) h.hizliYonSecim = 0; // ESC: önce seçimi iptal et else h.hizli3dAcik = false; } else if (!secimBekleniyor && !ilkKare && tik && disari && !ImGui::IsAnyItemActive()) { h.hizli3dAcik = false; } } // Bu karenin hover durumunu kaydet → sonraki kare öne-alma kararı (tooltip z-fix). oncekiHover = ImGui::IsAnyItemHovered(); ImGui::End(); ImGui::PopStyleVar(6); ImGui::PopStyleColor(10); } namespace { // Eksen/EksenSistemi düğümüne özel Hızlı 3D widget: mesh yok → ağırlık/kütük anlamsız. // Bunun yerine kompakt kart: orijin + dünya yönü (renkli XYZ) + Seç/Odakla/Gizle eylemleri. // Standart widget'la aynı mouse-merkezli açılış; kapanış Kapat butonu / ESC ile. bool eksenHizliWidgetCiz(UygulamaBaglam& b) { HizliUIDurum& h = b.hizli; Sahne& sahne = b.sahne; Dugum* hd = sahne.bul(h.hizli3dKimlik); if (!hd) { h.hizli3dAcik = false; return true; } const ui::Tema& t = tema(); const Vec3d org = hd->donusum.konum; // orijin (dünya) const Vec3d yon = hd->donusum.yonelim.dondur({1, 0, 0}); // eksen dünya yönü (yerel +X) const ImVec4 exR[3] = { {0.95f, 0.35f, 0.38f, 1.0f}, {0.40f, 0.92f, 0.50f, 1.0f}, {0.35f, 0.60f, 1.0f, 1.0f}}; ImGui::PushStyleColor(ImGuiCol_WindowBg, karisim(t.panel, t.arka, 0.30f)); ImGui::PushStyleColor(ImGuiCol_Border, karisim(t.vurgu, t.vurgu2, 0.40f)); ImGui::PushStyleColor(ImGuiCol_Text, t.metin); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 10.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.5f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{12, 10}); const ImGuiViewport* anaVp = ImGui::GetMainViewport(); ImGui::SetNextWindowViewport(anaVp->ID); constexpr float W = 236.0f; { static f32 sonAcX = 1e30f, sonAcY = 1e30f; const bool yeni = std::fabs(h.hizli3dAcilisX - sonAcX) > 0.5f || std::fabs(h.hizli3dAcilisY - sonAcY) > 0.5f; if (yeni && (h.hizli3dAcilisX != 0 || h.hizli3dAcilisY != 0)) { sonAcX = h.hizli3dAcilisX; sonAcY = h.hizli3dAcilisY; ImGui::SetNextWindowPos({h.hizli3dAcilisX - W * 0.5f, h.hizli3dAcilisY - 20.0f}, ImGuiCond_Always); } else { ImGui::SetNextWindowPos({b.vpRectMinX + 70, b.vpRectMinY + 70}, ImGuiCond_FirstUseEver); } } ImGui::SetNextWindowSize({W, 0}, ImGuiCond_Always); ImGui::Begin("###hizli3dEksen", &h.hizli3dAcik, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoFocusOnAppearing); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); ImGui::PushStyleColor(ImGuiCol_Text, t.vurgu); ImGui::Text("%s %s", tipSimge(hd->tip), hd->ad.empty() ? tipAdi(hd->tip) : hd->ad.c_str()); ImGui::PopStyleColor(); ImGui::Separator(); const auto xyzSatir = [&](const char* etiket, const Vec3d& v) { ImGui::TextColored(t.metinSoluk, "%s", etiket); ImGui::SameLine(64); ImGui::TextColored(exR[0], "%.2f", v.x); ImGui::SameLine(); ImGui::TextColored(exR[1], "%.2f", v.y); ImGui::SameLine(); ImGui::TextColored(exR[2], "%.2f", v.z); }; xyzSatir("Orijin", org); xyzSatir("Yön", yon); ImGui::Separator(); const float bw = (W - 24.0f - 8.0f) * 0.5f; if (ImGui::Button("⊹ Seç", {bw, 0})) sahne.sec({h.hizli3dKimlik, 0}, false); ImGui::SameLine(); if (ImGui::Button("⛶ Odakla", {bw, 0})) b.kamera.sigdir(hd->dunyaMerkez(), hd->dunyaYaricap() + 0.6f); if (ImGui::Button(hd->gorunur ? "◉ Gizle" : "○ Göster", {bw, 0})) b.gorunurDegistir(h.hizli3dKimlik, !hd->gorunur); ImGui::SameLine(); if (ImGui::Button("✕ Kapat", {bw, 0})) h.hizli3dAcik = false; if (ImGui::IsKeyPressed(ImGuiKey_Escape)) h.hizli3dAcik = false; ImGui::End(); ImGui::PopStyleColor(3); ImGui::PopStyleVar(3); return true; } } // namespace } // namespace fiko