#include "urun_agaci_paneli.hpp" #include "fiko/ayar/ayar_sistemi.hpp" #include "ayar_kalici.hpp" // pencereDisariTasima() #include "malzeme_islem.hpp" #include "malzeme_nesnesi.hpp" // MalzemeNesnesi — ağaçtaki gerçek malzeme feature düğümü // MalzemeAtamaIslem (ağaç atamasının undo'su) #include "malzeme_penceresi.hpp" // malzemePencereAc — ağaç sağ-tık malzeme menüsü #include #include #include #include #include #include #include #include #include #include #include "analiz_urun_agaci.hpp" #include "cam_operasyon_penceresi.hpp" #include "dosya_islemleri.hpp" #include "cam_parca_op_penceresi.hpp" #include "cam_urun_agaci.hpp" #include "strafor_penceresi.hpp" #include "fiko/analiz/analiz_nesne.hpp" #include "fiko/analiz/analiz_sistemi.hpp" #include "fiko/cam/cam_sistemi.hpp" #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/cekirdek/olay.hpp" #include "fiko/gsd/gsd_feature.hpp" #include "fiko/platform/pencere.hpp" #include "fiko/sahne/grup_nesne.hpp" #include "fiko/sahne/olcum_sistemi.hpp" #include "fiko/sahne/parametre_nesne.hpp" #include "feature_penceresi.hpp" #include "fiko/parca/feature.hpp" #include "fiko/parca/parca_islemleri.hpp" #include "fiko/parca/part_design_sistemi.hpp" #include "fiko/sahne/sahne.hpp" #include "fiko/sahne/sahne_islem.hpp" #include "fiko/sahne/sketch_nesne.hpp" #include "fiko/sahne/parametric_tree.hpp" // Feature → Sketch/Parametre bağlantıları #include "sketch_ui.hpp" #include "fiko/ui/agac_gorunum.hpp" #include "fiko/ui/agac_simge_kayit.hpp" #include "fiko/ui/bilesen_ek.hpp" // ui::toastGoster (bağımlılık göster geri bildirimi) #include "fiko/ui/pencere_cerceve.hpp" #include "imgui.h" #include "uygulama_baglam.hpp" namespace fiko { namespace { // Ağaç yeniden-kurma bayrağı (perf): kaynak+dönüştürülmüş ağaç ve `harita` yalnız // YAPI/DURUM değişince (ECS mutasyon olayları + panel-içi düzenleme) yeniden kurulur; // aksi halde önceki kare cache'i çizilir → boşta kare rebuild etmez. İlk kare kirli. bool g_agacKirli = true; // Feature kopya-panosu (Kopyala/Yapıştır): son kopyalanan feature'ın tam durumu (tip+param+ // sketch/hedef). Yapıştır YENİ feature olarak kurar (featureBaslat+featureParamToplu+onayla). parca::PartDesignSistemi::FeatureYedek g_featKopya; bool g_featKopyaVar = false; // Ağaç klavye odağında mı (geçen kare) — global Delete işleyicisi bunu okuyup ağaç odaktaysa // gövde-silme (seciliSil) yerine ağaç seçimini sildirir (dugumSil, tüm tipler). Win32 tuş // mesajı frame BAŞINDA (panel çiziminden önce) geldiğinden geçen-kare değeri kullanılır (odak korunur). bool g_agacKlavyeOdakta = false; // ---- Ürün Ağacı görünüm ayarları (Ayarlar → "Ürün Ağacı" kategorisi) ---- // kaydetAgac() ile kayıt edilir, urunAgaciIcerikCiz() her kare agacUi'ya uygular. bool g_agacGirintiCizgileri = true; // dal/girinti bağlantı çizgileri (agac.girinti_cizgileri) bool g_agacSolKaydirma = true; // kaydırma çubuğu sol kenarda (agac.sol_kaydirma_cubugu) f32 g_agacSatirYukseklik = 0.0f; // satır yüksekliği; 0 = otomatik (agac.satir_yukseklik) bool g_agacYenidenAdlandirma = true; // inline yeniden adlandırma etkin mi (agac.yeniden_adlandirma) bool g_agacGorunurlukSutunu = true; // 👁 görünürlük sütunu (agac.gorunurluk_sutunu) bool g_agacVarsayilanHepsiAcik = false; // ilk açılışta tüm dallar açık mı (agac.varsayilan_hepsi_acik) // Sanal (VARLIK_YOK) düğümler için kararlı, ÇAKIŞMASIZ kimlik: etiket + tüm ayırt // edici alanların TAM 64-bit FNV-1a hash'i (eski 20-bit kırpma → ImGui "same id" // çakışması yapıyordu). Geometri düğümleri doğrudan varlik kimliğini kullanır. u64 dugumKimlik(const AgacDugum& d) { if (d.varlik != VARLIK_YOK) return static_cast(d.varlik); u64 h = 0xCBF29CE484222325ull; // FNV-1a const auto kat = [&](u64 x) { // 8 baytı sırayla karıştır for (int i = 0; i < 8; ++i) { h = (h ^ (x & 0xFF)) * 0x100000001B3ull; x >>= 8; } }; for (unsigned char c : d.etiket) h = (h ^ c) * 0x100000001B3ull; kat(d.camKimlik); kat(d.camTip); kat(static_cast(d.sketchOge)); kat(d.sketchOgeKimlik); return (1ull << 63) | (h & 0x7FFFFFFFFFFFFFFFull); // bit 63 = sanal işareti } // CAM sanal düğüm tip glifi (cam_urun_agaci.hpp enum yorumlarıyla hizalı). const char* camGlif(u8 tip) { switch (tip) { case CAM_PARTOP: return "⚙"; case CAM_AXIS: return "✛"; case CAM_STOK: return "▦"; case CAM_OP: return "▣"; case CAM_TAKIM: return "🔧"; case CAM_YOL: return "〰"; default: return nullptr; } } // CAM sanal düğüm → satır arketipi (sağlayıcısız düğümler; provider olanlar kendi verir). Arketip camArketip(u8 tip) { switch (tip) { case CAM_OP: return Arketip::CamOp; case CAM_TAKIM: return Arketip::CamTakim; case CAM_PARTOP: return Arketip::Grup; // Manufacturing Program dalı → kalın grup default: return Arketip::Genel; } } // Arketip → merkezî simge kaydı anahtarı (kullanıcı override'ı çözmek için). const char* agacSimgeAnahtar(Arketip a) { switch (a) { case Arketip::Govde: return "govde"; case Arketip::Sketch: return "sketch"; case Arketip::Feature: return "feature"; case Arketip::Datum: return "datum"; case Arketip::Tel: return "tel"; case Arketip::Yuzey: return "yuzey"; case Arketip::Grup: return "grup"; case Arketip::Olcum: return "olcum"; case Arketip::Parametre: return "parametre"; case Arketip::Analiz: return "analiz"; case Arketip::Isik: return "isik"; case Arketip::Konum: return "konum"; case Arketip::Malzeme: return "malzeme"; case Arketip::CamOp: return "cam_op"; case Arketip::CamTakim: return "cam_takim"; default: return nullptr; } } // Renk çipi hangi arketiplerde gösterilir (Feature/Sketch ikon kullanır, çip yok). bool cipliArketip(Arketip a) { return a == Arketip::Govde || a == Arketip::Datum || a == Arketip::Tel || a == Arketip::Yuzey || a == Arketip::Grup; // Grup: renk-kodlu klasör (agacRengiVar gate) } // Ölçüm türü → satır glifi (kumpas/cetvel görsel dili; CATIA Measures dalı). const char* olcumGlif(OlcumTur t) { switch (t) { case OlcumTur::Aci: return "∠"; case OlcumTur::Yaricap: return "◗"; case OlcumTur::Cap: return "⌀"; case OlcumTur::Alan: return "▧"; case OlcumTur::Koordinat: return "✛"; default: return "📏"; // Mesafe/Uzunluk } } // Kalıcı ölçümü kimliğinden bul (sanal ağaç düğümü → OlcumSistemi kaydı köprüsü). Olcum* olcumBul(OlcumSistemi& olcum, u32 kimlik) { for (Olcum& o : olcum.hepsi()) if (o.kimlik == kimlik) return &o; return nullptr; } // ASCII büyük/küçük duyarsız arama (filtre; Türkçe aksan eşleme gerekmez — // kullanıcı yazdığı biçimle eşleşir, ASCII kısmı katlanır). bool icerirDuyarsiz(const std::string& metin, const char* aranan) { if (!aranan || !aranan[0]) return true; const size_t an = std::strlen(aranan); if (metin.size() < an) return false; for (size_t i = 0; i + an <= metin.size(); ++i) { size_t j = 0; while (j < an && std::tolower((unsigned char)metin[i + j]) == std::tolower((unsigned char)aranan[j])) ++j; if (j == an) return true; } return false; } // Gelişmiş filtre (Onshape tarzı ":komut arg"): tip/durum/görünürlük filtreleri. Düz metin → // etiket alt-dize eşleşmesi. Bilinmeyen komut → düz metne düşer (yazarken kırılmaz). // :type feature tipi/arketip içerir (ör. :type fillet) // :errors yalnız hatalı feature'lar :suppressed bastırılmışlar // :hidden gizli öğeler :shown görünür öğeler bool filtreUygun(const std::string& etiket, DokumanNesnesi* n, GorunumSaglayici* gv, const char* filtre) { if (!filtre || !filtre[0]) return true; if (filtre[0] == ':') { std::string f = filtre + 1; const size_t sp = f.find(' '); std::string komut = f.substr(0, sp); const std::string arg = (sp == std::string::npos) ? std::string{} : f.substr(sp + 1); for (char& c : komut) c = (char)std::tolower((unsigned char)c); if (komut == "errors" || komut == "hata") return gv && gv->agacDurum() == Durum::Hata; if (komut == "suppressed" || komut == "bastik") return gv && gv->agacDurum() == Durum::Bastik; if (komut == "hidden" || komut == "gizli") return gv && !gv->gorunur(); if (komut == "shown" || komut == "gorunur") return gv && gv->gorunur(); if (komut == "type" || komut == "tip") { std::string tip; if (auto* fe = dynamic_cast(n)) tip = fe->tip(); else if (n) tip = n->agacSimgeAnahtar(); return arg.empty() || icerirDuyarsiz(tip, arg.c_str()); } // bilinmeyen komut → düz metin eşleşmesine düş (kolon dahil literal aranır) } return icerirDuyarsiz(etiket, filtre); } // Öğe-bazlı simge editörü popup'ı (sağ-tık "🎨 Simge Değiştir"). Durum caller'da // tutulur (duzenle=düğüm id, ac=bu kare aç); god-function'dan ayrıldı (modülerlik). void ogeSimgeEditorCiz(Cekirdek& cekirdek, UygulamaBaglam& b, u64& duzenle, bool& ac, char* buf, size_t bufN) { if (ac) { ImGui::OpenPopup("##ogeSimge"); ac = false; } if (!ImGui::BeginPopup("##ogeSimge")) return; ImGui::TextDisabled("🎨 Öğe Simgesi (bu düğüm)"); ImGui::Separator(); const ui::Simge pv = ui::AgacSimgeKayit::global().ogeAl(duzenle); const float fh = ImGui::GetFrameHeight(); if (pv.doku) ImGui::Image(pv.doku, {fh, fh}); else { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted(pv.glif && pv.glif[0] ? pv.glif : "·"); } ImGui::SameLine(); ImGui::SetNextItemWidth(190); ImGui::InputTextWithHint("##ogeSimgeGiris", "emoji veya .svg/.png yolu", buf, bufN); ImGui::SameLine(); if (ImGui::Button("📁")) { const std::string yol = resimDosyaSec((HWND)b.pencere.dogalTutamac()); if (!yol.empty()) std::snprintf(buf, bufN, "%s", yol.c_str()); } ImGui::Separator(); if (ImGui::Button("Uygula")) { ui::AgacSimgeKayit::global().ogeAyarla(duzenle, buf); // render önbelleği if (auto* gvo = cekirdek.gorunum((Varlik)duzenle)) gvo->agacSimgeOzelAyarla(buf); // objenin kalıtımsal UI özelliği (sahip) duzenle = 0; g_agacKirli = true; // simge değişimi olaysız → ağacı yeniden kur ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("Varsayılana Dön")) { ui::AgacSimgeKayit::global().ogeTemizle(duzenle); if (auto* gvo = cekirdek.gorunum((Varlik)duzenle)) gvo->agacSimgeOzelAyarla(""); buf[0] = 0; duzenle = 0; g_agacKirli = true; ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } // Döngü koruması: `olasiAta` gerçekten `v`'nin bir atası mı (yukarı zincir yürüyüşü). // Sürükle-bırakta bir düğümü kendi torununun içine taşımayı engeller. bool ataMi(Dunya& d, Varlik olasiAta, Varlik v) { Varlik c = v; for (int g = 0; g < 4096; ++g) { const HiyerarsiBilesen* h = d.bul(c); if (!h || h->ust == VARLIK_YOK) return false; if (h->ust == olasiAta) return true; c = h->ust; } return false; } // Sürükle-bırak sonucu → HiyerarsiTasiIslem kayıtları. K'yı hedefUst altına, H'ye // göre konum (-1 önce / +1 sonra / 0 sona=içine) yerleştirir; o ebeveynin TÜM // kardeşlerini 0..n-1 yeniden numaralandırır (urun_agaci ile aynı (sira,kimlik) dizisi). // Yalnız gerçekten değişen düğümler kayda girer (boş → no-op). std::vector tasiKayitlariKur(Dunya& d, Varlik K, Varlik H, Varlik hedefUst, int konum) { auto siraAl = [&](Varlik v) { const HiyerarsiBilesen* h = d.bul(v); return h ? h->sira : 0; }; std::vector kardes; const Depo& nesneler = d.depo(); for (size_t i = 0; i < nesneler.boyut(); ++i) { const Varlik v = nesneler.varlikAt(i); const HiyerarsiBilesen* h = d.bul(v); if ((h ? h->ust : VARLIK_YOK) == hedefUst && v != K) kardes.push_back(v); } std::sort(kardes.begin(), kardes.end(), [&](Varlik a, Varlik b) { const int sa = siraAl(a), sb = siraAl(b); return sa != sb ? sa < sb : a < b; }); size_t ekle = kardes.size(); // varsayılan: sona (içine bırakma) if (konum != 0) for (size_t i = 0; i < kardes.size(); ++i) if (kardes[i] == H) { ekle = konum < 0 ? i : i + 1; break; } kardes.insert(kardes.begin() + ekle, K); std::vector out; for (int s = 0; s < (int)kardes.size(); ++s) { const Varlik v = kardes[s]; const HiyerarsiBilesen* h = d.bul(v); const Varlik eskiUst = h ? h->ust : VARLIK_YOK; const int eskiSira = h ? h->sira : 0; if (eskiUst == hedefUst && eskiSira == s) continue; // değişmedi → atla out.push_back({v, eskiUst, hedefUst, eskiSira, s}); } return out; } // Bir düğüm + altındaki TÜM geometri gövdelerini toplar (grup seçimi / sığdır). void dugumGovdeleriTopla(const AgacDugum& d, Sahne& sahne, std::vector& out) { if (d.varlik != VARLIK_YOK && sahne.bul(d.varlik)) out.push_back(d.varlik); for (const AgacDugum& c : d.cocuklar) dugumGovdeleriTopla(c, sahne, out); } // Parametre düzenleme popup'ı (çift-tıkla açılan ad/ifade/birim editörü). Durum // caller'da (duzenle=parametre Varlik, ad/ifade/birim tamponları). God-function'dan ayrıldı. void paramDuzenlePopupCiz(Cekirdek& cekirdek, Sahne& sahne, u32& duzenle, char* ad, char* ifade, char* birim) { if (!duzenle) return; if (ImGui::BeginPopup("##paramDuzenle")) { auto* pn = dynamic_cast(cekirdek.nesne(duzenle)); if (!pn) { ImGui::CloseCurrentPopup(); } else { ImGui::TextDisabled("ƒ Parametre"); ImGui::Separator(); ImGui::SetNextItemWidth(180); ImGui::InputText("Ad", ad, 64); ImGui::SetNextItemWidth(180); const bool enter = ImGui::InputText("İfade", ifade, 128, ImGuiInputTextFlags_EnterReturnsTrue); ImGui::SetNextItemWidth(180); ImGui::InputText("Birim", birim, 16); ImGui::TextDisabled("= %g %s", pn->deger(), pn->birim().c_str()); // son çözülen if (ImGui::Button("Uygula") || enter) { pn->adAyarla(ad); pn->ifadeAyarla(ifade); pn->birimAyarla(birim); sahne.parametreleriCoz(); // yeniden çöz + ParametreDegisti → bağımlı feature recompute g_agacKirli = true; // ad/değer değişti → ağaç etiket/mini-bilgi tazele ImGui::CloseCurrentPopup(); } } ImGui::EndPopup(); } else { duzenle = 0; // popup kapandı } } // Bir ağaç düğümünü (+ tüm torunları) siler: gövdeler undo'lu SilIslem, grup kapları // sahne.sil, analiz nesnesi analizSis.sil. God-function'dan ayrıldı. void dugumSil(const AgacDugum& d, Cekirdek& cekirdek, Sahne& sahne, UygulamaBaglam& b) { const Varlik v = d.varlik; if (dynamic_cast(cekirdek.nesne(v))) { b.analizSis.sil(v); // analiz: renk override + kayıt düşer return; } // Düğüm + tüm torunlar: gövdeler undo'lu SilIslem, grup kapları sahne.sil. std::vector govdeIds, grupIds; std::function topla = [&](const AgacDugum& nn) { if (nn.varlik != VARLIK_YOK && nn.camTip == CAM_YOK) { if (sahne.bul(nn.varlik)) govdeIds.push_back(nn.varlik); else grupIds.push_back(nn.varlik); } for (const auto& c : nn.cocuklar) topla(c); }; topla(d); if (!govdeIds.empty()) cekirdek.uygula(std::make_unique(sahne, govdeIds)); for (u32 g : grupIds) sahne.sil(g); // grup/klasör kaplarını kaldır g_agacKirli = true; } // Sketch alt-öğe (Geometri/Kisit) bağlam menüsü — sketch'i doğrudan mutasyonlar // (çöz + ParametreDegisti). Çıkış-durumu yok → god-function'dan güvenle ayrıldı. void sketchOgeBaglamMenu(Cekirdek& cekirdek, UygulamaBaglam& b, const AgacDugum& d, Varlik aktifSketch) { auto* sk = dynamic_cast(cekirdek.nesne(d.camKimlik)); eskiz::EskizVeri* ev2 = sk ? &sk->eskiz() : nullptr; const bool aktif = d.camKimlik == aktifSketch; auto tazele = [&] { // mutasyon sonrası: çöz + bağımlı feature recompute if (ev2) ev2->coz(); cekirdek.olaylar().yayinla(ParametreDegisti{d.camKimlik}, "Sketch"); }; ImGui::TextDisabled("%s", d.etiket.c_str()); ImGui::Separator(); if (d.sketchOge == SketchOge::Geometri && ev2) { const eskiz::Geometri* g = ev2->geoBul(d.sketchOgeKimlik); if (ImGui::MenuItem("◎ Seç", nullptr, false, aktif)) { auto& ss = b.sketch.sketchSecili; if (std::find(ss.begin(), ss.end(), d.sketchOgeKimlik) == ss.end()) ss.push_back(d.sketchOgeKimlik); } const bool yapi = g && g->yapi; if (ImGui::MenuItem(yapi ? "▬ Normal'e Çevir" : "┈ Yapı'ya Çevir")) { ev2->yapiAyarla(d.sketchOgeKimlik, !yapi); tazele(); } ImGui::Separator(); if (ImGui::MenuItem("🗑 Sil")) { ev2->geoSil(d.sketchOgeKimlik); b.sketch.sketchSecili.clear(); b.sketch.sketchSeciliKisit.clear(); tazele(); } } else if (d.sketchOge == SketchOge::Kisit && ev2) { bool suru = true; for (const eskiz::Kisit& k : ev2->kisitlar()) if (k.kimlik == d.sketchOgeKimlik) { suru = k.suru; break; } if (ImGui::MenuItem(suru ? "⇄ Referansa Çevir" : "⇄ Sürücüye Çevir")) { ev2->kisitSuruAyarla(d.sketchOgeKimlik, !suru); tazele(); } ImGui::Separator(); if (ImGui::MenuItem("🗑 Sil")) { ev2->kisitSil(d.sketchOgeKimlik); b.sketch.sketchSeciliKisit.clear(); tazele(); } } else { ImGui::TextDisabled("(referans öğe — Origin/H/V yön)"); } } // Ölçüm bağlam menüsü (Seç+Sığdır / Sil). Çıkış-durumu yok → god-function'dan ayrıldı. void olcumBaglamMenu(UygulamaBaglam& b, Sahne& sahne, const AgacDugum& d) { ImGui::TextDisabled("📐 %s", d.etiket.c_str()); ImGui::Separator(); if (ImGui::MenuItem("🔍 Sığdır")) { if (Olcum* o = olcumBul(b.olcum, d.camKimlik)) { sahne.secimTemizle(); if (o->k1.govde && sahne.bul(o->k1.govde)) sahne.sec({o->k1.govde, 0}, true); if (o->k2.var && o->k2.govde && sahne.bul(o->k2.govde)) sahne.sec({o->k2.govde, 0}, true); if (!sahne.seciliGovdeler().empty()) b.secimiSigdir(); } } ImGui::Separator(); if (ImGui::MenuItem("🗑 Sil")) b.olcum.sil(d.camKimlik); } // Geometri/feature/grup düğümü bağlam menüsü (rename/sığdır/gizle/simge/suppress/lock/ // rollback/param/sil). Çıkış-durumları REF ile döner (ogeSimge* editör tetiği + silIstendi). void geometriDugumBaglamMenu(Cekirdek& cekirdek, UygulamaBaglam& b, Sahne& sahne, ui::AgacGorunum& agacUi, const AgacDugum& d, u64 baglamId, u64& ogeSimgeDuzenle, bool& ogeSimgeAc, char* ogeSimgeBuf, size_t ogeSimgeBufN, bool& silIstendi, int& gorunurEylem, int& topluBastir, bool& topluSil) { DokumanNesnesi* n = cekirdek.nesne(d.varlik); GorunumSaglayici* gv = cekirdek.gorunum(d.varlik); // ---- Malzeme (PartBody / grup / gövde fark etmez: alt-ağaçtaki TÜM gövdelere) ---- // Kullanıcı "PartBody altına malzeme atayamıyorum" dedi (07-20): pencereden geçmeden // ağaçta sağ-tık → uygula. Grup düğümü Dugum DEĞİLDİR; gövdeler dugumGovdeleriTopla // ile toplanır (görünürlük eylemlerinin kullandığı aynı yol). { std::vector malzemeHedef; dugumGovdeleriTopla(d, sahne, malzemeHedef); if (!malzemeHedef.empty() && ImGui::BeginMenu("🎨 Malzeme")) { const u32 mevcut = b.malzemeAta.govdeninMalzemesi(malzemeHedef[0]); // Undo: pencereyle AYNI kalıp (önce/sonra tam kopya) — ağaçtan yapılan atama da // Ctrl+Z'ye girer (aksi halde "geri alamıyorum" bug'ı doğar). const auto uygula = [&](u32 malzemeKimlik) { const malzeme::MalzemeAtama once = b.malzemeAta; for (u32 g : malzemeHedef) b.malzemeAta.govdeyeAta(g, malzemeKimlik); cekirdek.uygula(std::make_unique( b.malzemeAta, once, b.malzemeAta, malzemeKimlik ? "Malzeme Ata" : "Malzeme Kaldır")); malzemeDugumleriSenkron(cekirdek, sahne, b.malzemeKut, b.malzemeAta); g_agacKirli = true; // ağaç düğümü eklendi/silindi → yeniden kur // rozet tazelensin }; if (ImGui::MenuItem("✖ Malzemeyi Kaldır", nullptr, false, mevcut != 0)) uygula(0); ImGui::Separator(); malzeme::MalzemeSinif sonSinif = malzeme::MalzemeSinif::Diger; bool ilk = true; for (const malzeme::MalzemeTanim& m : b.malzemeKut.hepsi()) { if (ilk || m.sinif != sonSinif) { // sınıf başlığı (Metal/Plastik/…) if (!ilk) ImGui::Separator(); ImGui::TextDisabled("%s", malzeme::sinifAdi(m.sinif)); sonSinif = m.sinif; ilk = false; } if (ImGui::MenuItem(m.ad.c_str(), nullptr, m.kimlik == mevcut)) uygula(m.kimlik); } ImGui::Separator(); if (ImGui::MenuItem("⚙ Malzeme Penceresi…")) malzemePencereAc(); ImGui::EndMenu(); } } // ---- BODY (CATIA PartBody) eylemleri: In Work Object + yeni body (sağ-tık, çift-tık ek) ---- if (sahne.bodyMi(d.varlik)) { // In Work TEK'tir: set In Work iken body işaretli görünmez (tekrar seçilebilir). const bool aktif = !sahne.inWorkSetMi() && sahne.aktifBody() == d.varlik; if (ImGui::MenuItem("⊙ In Work Object Yap", nullptr, aktif, !aktif)) { sahne.aktifBodyAyarla(d.varlik); // sonraki sketch/feature/import bu body'ye gider g_agacKirli = true; } if (ImGui::MenuItem("➕ Yeni Body Ekle")) { sahne.yeniBody(); g_agacKirli = true; } ImGui::Separator(); } // ---- GEOMETRICAL SET (GSD) eylemleri: yeni set + aktif set (isim tabanlı tespit, // iskelet pointer'ından bağımsız → yükleme sonrası da çalışır). Konteyner "Geometrical // Sets" veya bireysel "Geometrical Set N" üzerinde gösterilir. ---- { const std::string setAd = n ? n->ad() : d.etiket; const bool gsKonteyner = (setAd == "Geometrical Sets"); // Bireysel set: "Geometrical Sets" konteynerinin çocuğu olan HER grup (yeniden // adlandırılmış/yüklenmiş setler dahil); ad öneki yedek tespit olarak kalır. bool gsSet = setAd.rfind("Geometrical Set ", 0) == 0; if (!gsSet && d.varlik != VARLIK_YOK && dynamic_cast(n)) { if (const HiyerarsiBilesen* h = sahne.dunya().bul(d.varlik)) if (const AdBilesen* ua = sahne.dunya().bul(h->ust)) gsSet = (ua->ad == "Geometrical Sets"); } if (gsKonteyner || gsSet) { if (gsSet) { // set In Work olur (sonraki GSD feature'ları buraya; body çizgisi kalkar) const bool aktif = sahne.inWorkSetMi() && sahne.aktifGeometrikSet() == d.varlik; if (ImGui::MenuItem("⊙ In Work Object Yap", nullptr, aktif, !aktif)) { sahne.aktifGeometrikSetAyarla(d.varlik); g_agacKirli = true; } } if (ImGui::MenuItem("➕ Yeni Geometrical Set")) { sahne.yeniGeometrikSet(); // yeni set + In Work yap g_agacKirli = true; } ImGui::Separator(); } } if (ImGui::MenuItem("✏ Yeniden Adlandır", nullptr, false, n != nullptr)) agacUi.renameBaslat(baglamId, n ? n->ad().c_str() : d.etiket.c_str()); if (ImGui::MenuItem("🔍 Sığdır")) b.secimiSigdir(); // Benzer Seç (CATIA "Select Similar"): aynı DugumTip'teki TÜM geometri gövdelerini seç → // toplu renklendirme/gizleme/işlem kolaylığı (ör. tüm importlar, tüm silindirler). if (Dugum* dd = sahne.bul(d.varlik); dd && dd->brep) { const DugumTip tip = dd->tip; if (ImGui::MenuItem("⊚ Benzer Seç (aynı tip gövdeler)")) { std::vector ayni; for (Dugum& x : sahne.dugumler()) if (x.brep && x.tip == tip) ayni.push_back(x.kimlik); if (!ayni.empty()) { if (sahne.kip() != SecimKip::Govde) sahne.kipAyarla(SecimKip::Govde); sahne.secimTemizle(); sahne.secTopluEkle(ayni); // TEK olay + O(N) } } } if (!d.cocuklar.empty()) { // dallı düğüm → alt ağacı toptan aç/kapat (büyük montaj) if (ImGui::MenuItem("⊞ Alt Ağacı Aç")) agacUi.altAgaciAc(baglamId, true); if (ImGui::MenuItem("⊟ Alt Ağacı Kapat")) agacUi.altAgaciAc(baglamId, false); } ImGui::Separator(); // ---- görünürlük grubu ---- const bool gorunur = gv ? gv->gorunur() : true; if (ImGui::MenuItem(gorunur ? "○ Gizle" : "◉ Göster", nullptr, false, gv != nullptr)) b.gorunurDegistir(d.varlik, !gorunur); // Çoklu seçim → toplu gizle/göster (seçili tüm öğelere uygula; caller ev.secili ile). if (const std::size_t sc = agacUi.seciliSayi(); sc > 1) { char et[40]; std::snprintf(et, sizeof(et), "○ %zu Öğeyi Gizle", sc); if (ImGui::MenuItem(et)) gorunurEylem = 3; std::snprintf(et, sizeof(et), "◉ %zu Öğeyi Göster", sc); if (ImGui::MenuItem(et)) gorunurEylem = 4; } // İzole (CATIA "Swap visible space"): bu düğüm dışındaki TÜM gövdeleri gizle. // Tersinir → "Tümünü Göster". Ağaç-genelinde iş → caller'da s_agacKok ile uygulanır. if (ImGui::MenuItem("◈ İzole Et (Sadece Bunu Göster)")) gorunurEylem = 1; if (ImGui::MenuItem("◍ Tümünü Göster")) gorunurEylem = 2; ImGui::Separator(); // ---- görünüm/simge ---- if (ImGui::MenuItem("🎨 Simge Değiştir…")) { // öğe-bazlı simge override ogeSimgeDuzenle = baglamId; ogeSimgeAc = true; std::snprintf(ogeSimgeBuf, ogeSimgeBufN, "%s", ui::AgacSimgeKayit::global().ogeHam(baglamId).c_str()); } // ---- Feature (Part Design) durum eylemleri: Suppress / Lock / Rollback ---- if (auto* feat = dynamic_cast(n)) { ImGui::Separator(); // Sırada Taşı (sürükle-sıralamanın menü ikizi): recompute sırasında komşu feature ile yer // değiştir. Bağımlılık DOĞRULANIR (featureSiraOner) — üretici sonra kalamaz → geçersizse uyarı. { const auto& sr = b.partD.featureSirasi(); int idx = -1; for (int i = 0; i < (int)sr.size(); ++i) if (sr[i] == d.varlik) { idx = i; break; } const auto siraTasi = [&](u32 komsu, bool oncesine) { std::vector yeni; if (!b.partD.featureSiraOner(d.varlik, komsu, oncesine, yeni)) { ui::toastGoster("Geçersiz sıra (bağımlılık ihlali — üretici sonra kalamaz)", ui::ToastTip::Uyari); return; } std::vector eski = b.partD.featureSirasi(); // ayarla ÖNCESİ (oner değiştirmez) b.partD.featureSirasiniAyarla(yeni); cekirdek.uygula(std::make_unique(b.partD, std::move(eski), std::move(yeni))); g_agacKirli = true; }; if (idx > 0) { const u32 onceki = sr[idx - 1]; if (ImGui::MenuItem("⬆ Sırada Yukarı Taşı")) siraTasi(onceki, true); } if (idx >= 0 && idx < (int)sr.size() - 1) { const u32 sonraki = sr[idx + 1]; if (ImGui::MenuItem("⬇ Sırada Aşağı Taşı")) siraTasi(sonraki, false); } } const bool bastik = feat->bastirildi(); if (ImGui::MenuItem(bastik ? "▶ Aç (Unsuppress)" : "⏸ Bastır (Suppress)")) { cekirdek.uygula(std::make_unique(b.partD, d.varlik, !bastik)); g_agacKirli = true; } const bool kilit = feat->kilitli(); if (ImGui::MenuItem(kilit ? "🔓 Kilidi Aç" : "🔒 Kilitle")) { b.partD.kilitAyarla(d.varlik, !kilit); g_agacKirli = true; } const bool rollbackBurada = b.partD.rollbackNoktasi() == d.varlik; if (ImGui::MenuItem("⏪ Buraya Geri Sar (Rollback)", nullptr, rollbackBurada)) { b.partD.rollbackAyarla(d.varlik); g_agacKirli = true; } if (b.partD.rollbackNoktasi() != 0) if (ImGui::MenuItem("⏩ Sona Sar (Rollback İptal)")) { b.partD.rollbackAyarla(0); g_agacKirli = true; } // Bağımlılıkları Göster (CATIA/Onshape "Show dependencies"): bu feature'ı besleyen // KAYNAK (upstream: hedefDugum zinciri) + ona bağımlı ARDIL (downstream) feature'ları // sahnede seç → hem ürün ağacı hem 3D bunları vurgular ("silersem ne kırılır?"). if (ImGui::MenuItem("🔗 Bağımlılıkları Göster")) { std::vector zincir; // vurgulanacak sonuç gövde id'leri std::unordered_set gorulen; const auto uretici = [&](u32 dugum) -> parca::Feature* { // sonucDugum==dugum olan feature for (u32 fk : b.partD.featureler()) if (parca::Feature* g = b.partD.bul(fk); g && g->sonucDugum() == dugum) return g; return nullptr; }; for (parca::Feature* c = feat; c;) { // upstream: girdi zincirini geri yürü parca::Feature* p = uretici(c->hedefDugum()); if (!p || gorulen.count(p->kimlik())) break; gorulen.insert(p->kimlik()); if (p->sonucDugum()) zincir.push_back(p->sonucDugum()); c = p; } std::vector yigin{feat}; // downstream: bağımlı ardılları BFS gorulen.insert(feat->kimlik()); while (!yigin.empty()) { parca::Feature* f0 = yigin.back(); yigin.pop_back(); for (u32 fk : b.partD.featureler()) if (parca::Feature* h = b.partD.bul(fk); h && h->hedefDugum() == f0->sonucDugum() && !gorulen.count(h->kimlik())) { gorulen.insert(h->kimlik()); if (h->sonucDugum()) zincir.push_back(h->sonucDugum()); yigin.push_back(h); } } sahne.secimTemizle(); if (feat->sonucDugum()) sahne.sec({feat->sonucDugum(), 0}, true); // kendisi for (u32 bg : zincir) sahne.sec({bg, 0}, true); ui::toastGoster(zincir.empty() ? "Bağımlılık yok (bağımsız feature)" : "Bağımlılıklar vurgulandı", ui::ToastTip::Bilgi); } ImGui::Separator(); // Kopyala/Yapıştır (feature): kopyalanan feature tip+param olarak YENİ feature kurar // (bu feature'ın sonuç gövdesi üzerine stack'lenir). Aynı işlemi tekrar uygulamak için. if (ImGui::MenuItem("📋 Kopyala")) { g_featKopya = b.partD.yedekle(d.varlik); g_featKopyaVar = !g_featKopya.tip.empty(); ui::toastGoster(g_featKopyaVar ? "Feature kopyalandı" : "Kopyalanamadı", g_featKopyaVar ? ui::ToastTip::Basari : ui::ToastTip::Hata); } if (ImGui::MenuItem("📄 Yapıştır", nullptr, false, g_featKopyaVar)) { const u32 hedef = feat->sonucDugum() ? feat->sonucDugum() : feat->hedefDugum(); const u32 yeni = b.partD.featureBaslat(g_featKopya.tip, g_featKopya.sketch, hedef); if (yeni) { std::vector> ps(g_featKopya.parametreler.begin(), g_featKopya.parametreler.end()); if (!ps.empty()) b.partD.featureParamToplu(yeni, ps); b.partD.featureOnayla(yeni); g_agacKirli = true; ui::toastGoster("Feature yapıştırıldı", ui::ToastTip::Basari); } else ui::toastGoster("Yapıştırma başarısız (uygun hedef gövde yok)", ui::ToastTip::Hata); } } // ---- ÇOKLU SEÇİM: toplu Suppress/Aç (seçili tüm feature'lara; caller ev.secili ile uygular) ---- if (const std::size_t sc = agacUi.seciliSayi(); sc > 1) { ImGui::Separator(); char et[48]; std::snprintf(et, sizeof(et), "⏸ %zu Öğeyi Bastır (Suppress)", sc); if (ImGui::MenuItem(et)) topluBastir = 1; std::snprintf(et, sizeof(et), "▶ %zu Öğeyi Aç (Unsuppress)", sc); if (ImGui::MenuItem(et)) topluBastir = 2; } // ---- Grup (Parameters / PartBody / Geometrical Set): parametre ekle ---- if (dynamic_cast(n)) { ImGui::Separator(); // Alt Klasör: bu grubun ALTINA iç içe boş klasör (undo'lu). Tree header 📁 üst-seviye yapar; // bu, mevcut grup/klasör altına yuvalar (öğeler DnD ile taşınır). if (ImGui::MenuItem("📁 Alt Klasör Ekle")) { cekirdek.uygula(std::make_unique(sahne, "Klasör", d.varlik)); g_agacKirli = true; } if (ImGui::MenuItem("ƒ Yeni Parametre")) { sahne.parametreEkle("Parametre", 0.0, "mm", d.varlik); // grup altına yerel/global g_agacKirli = true; } // Renk-kodlu klasör (SolidWorks 2018): grup rengini ata/kaldır → ürün ağacında renk çipi. Dunya& dny = cekirdek.dunya(); if (ImGui::BeginMenu("🎨 Klasör Rengi")) { Vec3f mevcut{0.55f, 0.65f, 0.8f}; if (const GrupRenkBilesen* r = dny.bul(d.varlik)) mevcut = r->renk; f32 c[3] = {mevcut.x, mevcut.y, mevcut.z}; if (ImGui::ColorPicker3("##klasorRenk", c, ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoInputs)) { dny.ekle(d.varlik, GrupRenkBilesen{{c[0], c[1], c[2]}}); g_agacKirli = true; // çip renk build'de okunur (dugumDonustur → agacRengi) } if (dny.var(d.varlik) && ImGui::MenuItem("Rengi Kaldır")) { dny.depo().sil(d.varlik); g_agacKirli = true; } ImGui::EndMenu(); } } ImGui::Separator(); if (const std::size_t sc = agacUi.seciliSayi(); sc > 1) { // çoklu seçim → toplu sil char et[32]; std::snprintf(et, sizeof(et), "🗑 %zu Öğeyi Sil", sc); if (ImGui::MenuItem(et)) topluSil = true; if (ImGui::IsItemHovered()) ImGui::SetTooltip("Seçili TÜM öğeleri (ve alt öğelerini) siler"); } else { if (ImGui::MenuItem("🗑 Sil")) silIstendi = true; if (!d.cocuklar.empty() && ImGui::IsItemHovered()) ImGui::SetTooltip("Bu düğümü ve TÜM alt öğelerini (çocuklar) siler"); } } // Ağaç düğümüne çift-tık: CAM/analiz penceresi aç · feature düzenle · sketch gir · // parametre popup (çıkış: paramDuzenle+ad/ifade/birim REF) · ölçüm kaynağına git · sığdır. void agacCiftTikIsle(Cekirdek& cekirdek, Sahne& sahne, UygulamaBaglam& b, const AgacDugum& d, u32& paramDuzenle, char* paramAd, char* paramIfade, char* paramBirim) { // Strafor sezon body'sine çift-tık → o sezonun düzenleme penceresi (tek pencere, tek sezon). if (d.varlik != VARLIK_YOK && straforSezonMu(d.varlik)) { straforSezonAc(d.varlik); sahne.aktifBodyAyarla(d.varlik); // In Work Object da olsun (yeni Diz buraya gider) g_agacKirli = true; return; } if (d.camTip == CAM_OP || d.camTip == CAM_TAKIM || d.camTip == CAM_YOL) { camOperasyonPencereAc(d.camKimlik); } else if (d.camTip == CAM_PARTOP || d.camTip == CAM_AXIS || d.camTip == CAM_STOK) { camParcaOpPencereAc(d.camKimlik); } else if (d.arketip == Arketip::Olcum && d.varlik == VARLIK_YOK) { // Ölçüme çift-tık: referans gövdeleri seç + sığdır (kaynağa git). if (Olcum* o = olcumBul(b.olcum, d.camKimlik)) { sahne.secimTemizle(); if (o->k1.govde && sahne.bul(o->k1.govde)) sahne.sec({o->k1.govde, 0}, true); if (o->k2.var && o->k2.govde && sahne.bul(o->k2.govde)) sahne.sec({o->k2.govde, 0}, true); if (!sahne.seciliGovdeler().empty()) b.secimiSigdir(); } } else if (d.varlik != VARLIK_YOK && sahne.bodyMi(d.varlik)) { // CATIA "Define In Work Object": body'ye ÇİFT-TIK → AKTİF body. Sonraki sketch/feature/import // bu body'ye gider. Ağaç tazelensin (In Work Object işareti güncellensin). sahne.aktifBodyAyarla(d.varlik); g_agacKirli = true; } else if (d.varlik != VARLIK_YOK) { // Feature (kendisi) veya bir feature'ın sonuç düğümü → düzenleme penceresi. u32 featKimlik = 0; if (dynamic_cast(cekirdek.nesne(d.varlik))) { featKimlik = d.varlik; // düğümün kendisi bir Feature } else { for (u32 fk : b.partD.featureler()) if (parca::Feature* f = b.partD.bul(fk); f && f->sonucDugum() == d.varlik) { featKimlik = fk; // düğüm bir feature'ın sonuç gövdesi break; } } if (featKimlik) { featurePencereAc(featKimlik); } else if (auto* pn = dynamic_cast(cekirdek.nesne(d.varlik))) { // Parametre düğümü → düzenleme popup'ı (ad / ifade / birim). paramDuzenle = d.varlik; std::snprintf(paramAd, 64, "%s", pn->ad().c_str()); std::snprintf(paramIfade, 128, "%s", pn->ifade().c_str()); std::snprintf(paramBirim, 16, "%s", pn->birim().c_str()); ImGui::OpenPopup("##paramDuzenle"); } else { Dugum* dd = sahne.bul(d.varlik); if (analizAgacCiftTik(cekirdek, b.analizSis, d.varlik)) { // Analiz/kesit düğümü kendi penceresini açtı. } else if (dd && dd->tip == DugumTip::Sketch) { b.sketchGir(d.varlik); } else if (dd && dd->tip == DugumTip::Pafta) { // Çift tık TOGGLE: içindeyken aynı düğüme çift tık moddan ÇIKARIR (sketch ikizi). if (b.paftaAktifMi && b.paftaAktifMi(d.varlik)) { if (b.paftaCik) b.paftaCik(); } else if (b.paftaGir) { b.paftaGir(d.varlik); } } else { b.secimiSigdir(); } } } } // Hızlı eylem kimlikleri (satır-sağı butonları) — file scope: setup + handler + maske ortak. enum : u64 { HE_SIGDIR = 1, HE_AD = 2, HE_PENCERE = 3 }; enum : u32 { HM_SIGDIR = 1u << 0, HM_AD = 1u << 1, HM_PENCERE = 1u << 2 }; // Satır-sağı hızlı eylem: sığdır / yeniden adlandır / CAM pencere aç. void hizliEylemIsle(Cekirdek& cekirdek, Sahne& sahne, UygulamaBaglam& b, ui::AgacGorunum& agacUi, const AgacDugum& d, u64 eylemId, u64 satirId) { switch (eylemId) { case HE_SIGDIR: { std::vector hedef; dugumGovdeleriTopla(d, sahne, hedef); if (!hedef.empty()) { sahne.secimTemizle(); sahne.secTopluEkle(hedef); // TEK olay + O(N) b.secimiSigdir(); } break; } case HE_AD: { DokumanNesnesi* n = d.varlik != VARLIK_YOK ? cekirdek.nesne(d.varlik) : nullptr; if (n) agacUi.renameBaslat(satirId, n->ad().c_str()); break; } case HE_PENCERE: if (d.camTip == CAM_OP || d.camTip == CAM_TAKIM || d.camTip == CAM_YOL) camOperasyonPencereAc(d.camKimlik); else if (d.camTip == CAM_PARTOP || d.camTip == CAM_AXIS || d.camTip == CAM_STOK) camParcaOpPencereAc(d.camKimlik); break; } } // Kaynak AgacDugum → ui::AgacDugum dönüşümü + filtre budaması (eşleşen VEYA eşleşen // torunu olan düğümler kalır) + zengin-satır pull (arketip/durum/mini-bilgi/rozet/simge). // harita: id→kaynak düğüm (olay işleme köprüsü) REF ile doldurulur. featureSonuc: // feature düğümlerinin sonuç-gövde kimliği (id→sonucDugum) — seçim senkronu her kare // dynamic_cast YAPMASIN diye kurulum-anında (nadir) hesaplanır. Dönüş: düğüm kalır mı. // Ağaç dönüşümü sırasında malzeme rozeti için okunan belge verisi. Fonksiyon // zinciri (dugumDonustur özyinelemeli) imza büyütmesin diye dosya-static; ağaç // KURULUMUNDAN hemen önce bağlanır (aynı karede, tek thread). const malzeme::MalzemeKutuphanesi* s_mKut = nullptr; const malzeme::MalzemeAtama* s_mAta = nullptr; bool dugumDonustur(const AgacDugum& d, ui::AgacDugum& out, Cekirdek& cekirdek, Sahne& sahne, std::unordered_map& harita, std::unordered_map& featureSonuc, const char* filtre, u32 rollbackFeat) { const Varlik v = d.varlik; DokumanNesnesi* n = v != VARLIK_YOK ? cekirdek.nesne(v) : nullptr; GorunumSaglayici* gv = v != VARLIK_YOK ? cekirdek.gorunum(v) : nullptr; out.etiket = n ? n->ad() : d.etiket; out.rollbackCizgi = (v != VARLIK_YOK && v == rollbackFeat); // rollback noktası → yatay çubuk // Feature sonuç gövdesi feature'a parent'lanır (part_design ustAyarla) → ağaçta ÇİFT görünür // (feature satırı + sonuç gövde satırı). CATIA gibi TEK satır kalsın: sonuç çocuğunu atla // (feature satırının kendisi sonucu temsil eder; 3D-pick highlight'ı featureSonuc ile korunur). Varlik atlaSonuc = VARLIK_YOK; if (auto* f = dynamic_cast(n)) atlaSonuc = f->sonucDugum(); for (const AgacDugum& c : d.cocuklar) { if (atlaSonuc != VARLIK_YOK && c.varlik == atlaSonuc) continue; // sonuç gövde çocuğu → gizle ui::AgacDugum cc; if (dugumDonustur(c, cc, cekirdek, sahne, harita, featureSonuc, filtre, rollbackFeat)) out.cocuklar.push_back(std::move(cc)); } if (!filtreUygun(out.etiket, n, gv, filtre) && out.cocuklar.empty()) return false; out.id = dugumKimlik(d); // İkon: view provider > sanal düğüm ikonu (d.simge) > CAM glifi. out.simge.glif = gv ? gv->agacSimgesi() : (d.simge ? d.simge : camGlif(d.camTip)); out.gorunur = gv ? gv->gorunur() : true; out.hizliMaske = d.arketip == Arketip::Olcum ? 0u // ölçüm satırı: hızlı-buton yok : d.camTip != CAM_YOK ? HM_PENCERE : n ? (HM_SIGDIR | HM_AD) : HM_SIGDIR; Dunya& dunya = sahne.dunya(); // Arketip: view provider > CAM tipi > sanal düğüm arketipi (Grup/Datum/Genel). out.arketip = gv ? gv->agacArketip() : (d.camTip != CAM_YOK ? camArketip(d.camTip) : d.arketip); // Polimorfik simge anahtarı (feature./gsd., boşsa arketip) + 3-katman çözüm. const std::string ozelKey = n ? n->agacSimgeAnahtar() : std::string{}; const char* simgeKey = !ozelKey.empty() ? ozelKey.c_str() : agacSimgeAnahtar(out.arketip); out.simge = ui::AgacSimgeKayit::global().coz(out.id, simgeKey, out.simge.glif); out.durum = gv ? gv->agacDurum() : Durum::Normal; if (gv) { out.miniBilgi = gv->agacMiniBilgi(dunya); gv->agacRozetleri(dunya, out.rozetler); if (gv->agacRengiVar() && cipliArketip(out.arketip)) { // renk çipi (arketip-kapılı) const Vec3f c = gv->agacRengi(); out.renk = ImVec4{c.x, c.y, c.z, 1.0f}; } } if (out.arketip == Arketip::Datum) out.soluk = true; // referans → soluk if (out.arketip == Arketip::Grup) { // grup → kalın ad + öğe sayısı out.kalin = true; if (!out.cocuklar.empty()) out.miniBilgi = std::to_string(out.cocuklar.size()) + " öğe"; } if (out.arketip == Arketip::Feature) out.varsayilanKapali = true; // sonuç düğümü ilk katlı // CAM aktivitesi (op): alt öğeleri (🔧 takım / 〰 Nc Programlar) ilk görünümde KATLI. // Çok operasyonlu APT'de ağaç 3 kat şişiyordu; aktivite listesi tek bakışta okunsun. if (d.camTip == CAM_OP && !out.cocuklar.empty()) out.varsayilanKapali = true; // In Work Object TEK'tir (CATIA): body VEYA geometrical set — ayıraç sahne.inWorkSetMi(). const bool inWork = sahne.inWorkSetMi() ? (v != VARLIK_YOK && v == sahne.aktifGeometrikSet()) : (v != VARLIK_YOK && v == sahne.aktifBody()); if (inWork) { out.kalin = true; out.altiCizili = true; } harita[out.id] = &d; if (auto* f = dynamic_cast(n)) featureSonuc[out.id] = f->sonucDugum(); return true; } } // namespace // Ağaç KLAVYE odağında mı (geçen kare). Global Delete işleyicisi ağaç odaktaysa gövde-silme // yerine ağaç seçimini (dugumSil, tüm tipler) sildirir → çift-silme önlenir. void agacKirlet() { g_agacKirli = true; } bool agacKlavyeOdakta() { return g_agacKlavyeOdakta; } void olcumAgacTopla(Cekirdek&, OlcumSistemi& olcum, std::vector& kok) { std::vector& hepsi = olcum.hepsi(); if (hepsi.empty()) return; // ölçüm yok → dal hiç görünmez (ağaç temiz) AgacDugum grup; grup.etiket = "Ölçümler"; grup.simge = "📐"; grup.arketip = Arketip::Grup; grup.cocuklar.reserve(hepsi.size()); for (const Olcum& o : hepsi) { AgacDugum n; n.etiket = !o.aciklama.empty() ? o.aciklama : !o.metin.empty() ? o.metin : ("Ölçüm." + std::to_string(o.kimlik)); n.simge = olcumGlif(o.tur); n.arketip = Arketip::Olcum; n.camKimlik = o.kimlik; // sanal düğüm disambiguator + kimlik (panel: Seç/Sil) grup.cocuklar.push_back(std::move(n)); } kok.push_back(std::move(grup)); } void urunAgaciPaneliCiz(UygulamaBaglam& b) { // İçerde-mod: ağaç 3B görünümün İÇİNDE overlay olarak çizilir (main.cpp) — // ayrık pencere açılmaz; içerik ortak urunAgaciIcerikCiz'ten gelir. if (!b.panelAgac || b.agacIcinde) { if (!b.panelAgac && !b.agacIcinde) g_agacKlavyeOdakta = false; // ağaç tümüyle kapalı → odak yok return; // overlay modunda urunAgaciIcerikCiz ayrıca çizilir (bayrağı taze tutar) } ui::Pencere pen{"Ürün Ağacı", &b.panelAgac, {.boyut = {320, 500}, .pinDurum = &b.agacUstte, .bayrak = 0}}; if (pen) urunAgaciIcerikCiz(b); } bool urunAgaciIcerikCiz(UygulamaBaglam& b) { Cekirdek& cekirdek = b.cekirdek; Sahne& sahne = b.sahne; cam::CamSistemi& camSis = b.camSis; // Kalıcı UI durumu (açık/kapalı dallar, seçim görünümü, rename). static ui::AgacGorunum agacUi; static char filtre[64] = ""; // Parametre düzenleme popup durumu (çift-tıkla açılan ad/ifade/birim editörü). static u32 s_paramDuzenle = 0; static char s_paramAd[64] = "", s_paramIfade[128] = "", s_paramBirim[16] = ""; agacUi.renameCiftTikla = false; // çift-tık = domain eylemi (sketch/CAM pencere) // ---- Ayarlar → "Ürün Ağacı": görünüm ayarlarını her kare uygula ---- agacUi.baglantiCizgileri = g_agacGirintiCizgileri; agacUi.solScrollbar = g_agacSolKaydirma; agacUi.satirYuksekligi = g_agacSatirYukseklik; agacUi.renameEtkin = g_agacYenidenAdlandirma; agacUi.gorunurlukSutunu = g_agacGorunurlukSutunu; static bool s_agacIlkKare = true; if (s_agacIlkKare) { if (g_agacVarsayilanHepsiAcik) agacUi.hepsiniAc(true); s_agacIlkKare = false; } // ---- Sketch alt-öğe etkileşimi (hover senkronu her kare sıfırlanır) ---- const Varlik aktifSketch = b.sketch.aktifSketch; b.sketch.disHoverGeo = 0; b.sketch.disHoverKisit = 0; agacUi.disHover = 0; // Sağ hızlı eylemler (satır hover'ında belirir; maske satır-başına). if (agacUi.hizliEylemler.empty()) { agacUi.hizliEylemler = {{HE_SIGDIR, {"🔍"}, "Sığdır"}, {HE_AD, {"✏"}, "Yeniden Adlandır"}, {HE_PENCERE, {"🗔"}, "Pencereyi Aç"}}; } // ---- Üst şerit: filtre + hepsini aç/kapat -------------------------------- // Overlay tık-geçişi: şerit de (satırlar gibi) interaktif → üstündeyken ağaç // tüketmeli. Şerit dikey bandını (üst→ağaç başı) ölç, boş-alan testine kat. const ImVec2 seritMin = ImGui::GetCursorScreenPos(); const float seritGen = ImGui::GetContentRegionAvail().x; ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 58); if (ImGui::InputTextWithHint("##agacFiltre", "🔍 Filtrele... (:type :errors :suppressed :hidden)", filtre, sizeof(filtre))) { g_agacKirli = true; // filtre budaması dönüştürülmüş ağaca işlenir if (filtre[0]) agacUi.hepsiniAc(true); // filtre daralttı → eşleşmeler görünür kalsın } if (ImGui::IsItemHovered()) ImGui::SetTooltip("Düz metin = ada göre ara. Komutlar:\n" ":type tipe göre (ör. :type fillet)\n" ":errors hatalılar · :suppressed bastırılmışlar\n" ":hidden gizliler · :shown görünürler"); ImGui::SameLine(); if (ImGui::SmallButton("⊞")) agacUi.hepsiniAc(true); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Hepsini aç"); ImGui::SameLine(); if (ImGui::SmallButton("⊟")) agacUi.hepsiniAc(false); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Hepsini kapat"); // ---- İzolasyon (Solo): seçili gövde(ler)i yalnız bırak; ötekini gizle. NON-DESTRUCTIVE: // eski görünürlük saklanır, kapatınca geri gelir. gorunurDegistir olaysız-set → undo kirletmez. ---- static bool s_izoleAktif = false; static std::vector> s_izoleKayit; // (kimlik, eski görünür) ImGui::SameLine(); if (ImGui::SmallButton(s_izoleAktif ? "🎯" : "◎")) { if (!s_izoleAktif) { const std::vector sec = sahne.seciliGovdeler(); if (!sec.empty()) { s_izoleKayit.clear(); for (Dugum& d : sahne.dugumler()) { if (!d.brep) continue; // yalnız geometri taşıyan düğüm (grup/datum/sketch dokunulmaz) GorunumSaglayici* gv = cekirdek.gorunum(d.kimlik); if (!gv) continue; s_izoleKayit.push_back({d.kimlik, gv->gorunur()}); b.gorunurDegistir(d.kimlik, std::find(sec.begin(), sec.end(), d.kimlik) != sec.end()); } s_izoleAktif = !s_izoleKayit.empty(); g_agacKirli = true; } } else { // izolasyonu kaldır → saklı görünürlüğü geri yükle for (auto& [k, gr] : s_izoleKayit) b.gorunurDegistir(k, gr); s_izoleKayit.clear(); s_izoleAktif = false; g_agacKirli = true; } } if (ImGui::IsItemHovered()) ImGui::SetTooltip(s_izoleAktif ? "İzolasyonu kaldır (görünürlüğü geri yükle)" : "Seçiliyi izole et — ötekini gizle (Solo)"); // Yeni Klasör (kullanıcı grubu): aktif body altına (yoksa Part kökü) boş klasör → undo'lu. // Kullanıcı öğeleri sürükle-bırak ile içine taşır (renk: sağ-tık "🎨 Klasör Rengi"). ImGui::SameLine(); if (ImGui::SmallButton("📁")) { const Varlik ust = sahne.aktifBody() ? sahne.aktifBody() : sahne.partKokAl(); cekirdek.uygula(std::make_unique(sahne, "Klasör", ust)); g_agacKirli = true; } if (ImGui::IsItemHovered()) ImGui::SetTooltip("Yeni Klasör ekle (öğeleri sürükleyerek içine al)"); // ---- Filtre preset: ⚠ tek-tık = yalnız HATALI feature'lar (:errors triyaj). Tekrar → temizle. ---- { const bool hataAktif = std::strcmp(filtre, ":errors") == 0; ImGui::SameLine(); if (hataAktif) ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{1.0f, 0.40f, 0.35f, 1.0f}); if (ImGui::SmallButton("⚠")) { if (hataAktif) filtre[0] = 0; else std::snprintf(filtre, sizeof(filtre), ":errors"); g_agacKirli = true; if (filtre[0]) agacUi.hepsiniAc(true); // eşleşmeler görünür kalsın } if (hataAktif) ImGui::PopStyleColor(); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Yalnız hatalı feature'ları göster (aç/kapat)"); } // Filtre preset: 🚫 tek-tık = yalnız GİZLİ öğeler (:hidden — gizlediklerini bul/geri getir). Tekrar → temizle. { const bool gizliAktif = std::strcmp(filtre, ":hidden") == 0; ImGui::SameLine(); if (gizliAktif) ImGui::PushStyleColor(ImGuiCol_Text, ImVec4{0.55f, 0.70f, 1.0f, 1.0f}); if (ImGui::SmallButton("🚫")) { if (gizliAktif) filtre[0] = 0; else std::snprintf(filtre, sizeof(filtre), ":hidden"); g_agacKirli = true; if (filtre[0]) agacUi.hepsiniAc(true); } if (gizliAktif) ImGui::PopStyleColor(); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Yalnız gizli öğeleri göster (aç/kapat)"); } // ---- Rollback ADIM barı (Onshape: geri-sar noktasını feature feature kaydır) ---- // Yalnız rollback AKTİFken görünür (nokta context menü "Buraya Geri Sar" ile konur). // ⏫ bir önceki · ⏬ bir sonraki (son feature→sona sar) · ⏭ sona sar (iptal). featureSirasi // (recompute sırası) üzerinde adımlar → rollbackAyarla. Saf caller mantığı (motor değişmez). if (b.partD.rollbackNoktasi() != 0) { const auto& sr = b.partD.featureSirasi(); int idx = -1; for (int i = 0; i < (int)sr.size(); ++i) if (sr[i] == b.partD.rollbackNoktasi()) { idx = i; break; } ImGui::SameLine(); ImGui::BeginDisabled(idx <= 0); if (ImGui::SmallButton("⏫") && idx > 0) { b.partD.rollbackAyarla(sr[idx - 1]); g_agacKirli = true; } ImGui::EndDisabled(); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Rollback: bir önceki feature"); ImGui::SameLine(); if (ImGui::SmallButton("⏬")) { if (idx >= 0 && idx < (int)sr.size() - 1) b.partD.rollbackAyarla(sr[idx + 1]); else b.partD.rollbackAyarla(0); // son feature → sona sar (rollback iptal) g_agacKirli = true; } if (ImGui::IsItemHovered()) ImGui::SetTooltip("Rollback: bir sonraki feature"); ImGui::SameLine(); if (ImGui::SmallButton("⏭")) { b.partD.rollbackAyarla(0); g_agacKirli = true; } if (ImGui::IsItemHovered()) ImGui::SetTooltip("Rollback: sona sar (iptal)"); } // Çoklu seçim göstergesi (2+ öğe seçiliyse satır sağında soluk sayaç). if (const std::size_t sc = agacUi.seciliSayi(); sc > 1) { char et[24]; std::snprintf(et, sizeof(et), "%zu seçili", sc); const float w = ImGui::CalcTextSize(et).x; ImGui::SameLine(0, 0); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x - w - 2); ImGui::TextDisabled("%s", et); } // ---- Kaynak ağaç (dinamik: kayıtlı TÜM sağlayıcılar — geometri/CAM/analiz/ // döküm...). PERF: her kare rebuild yerine YALNIZ mutasyonda kurulur. // Kaynak+dönüştürülmüş ağaç ve haritalar cache'lenir (statik); ECS mutasyon // olayları g_agacKirli'yi tetikler, boşta kareler cache'i çizer. static std::vector s_agacKok; // kaynak (cache) static std::unordered_map s_harita; // id→kaynak (cache) static std::unordered_map s_featureSonuc; // id→feature sonucDugum (0 yok) // Ağaç yapısını/durumunu etkileyen ECS olaylarına bir kez abone ol → mutasyonda // g_agacKirli set (yüksek-frekans seçim/hover/jest/kare olayları HARİÇ; onlar cache'i // bozmaz). KomutCalisti geniş güvenlik ağı (aktif-gövde vb. olaysız değişimler için). static bool s_abone = false; if (!s_abone) { cekirdek.olaylar().abone( {OlayTuru::VarlikEklendi, OlayTuru::VarlikSilindi, OlayTuru::DonusumDegisti, OlayTuru::BrepDegisti, OlayTuru::GorunurlukDegisti, OlayTuru::RenkDegisti, OlayTuru::DokumanAcildi, OlayTuru::OlcumEklendi, OlayTuru::OlcumSilindi, OlayTuru::WorkbenchDegisti, OlayTuru::SketchGirildi, OlayTuru::SketchCikildi, OlayTuru::ParametreDegisti, OlayTuru::HesaplamaBitti, OlayTuru::SketchCozuldu, OlayTuru::CamIslemEklendi, OlayTuru::CamIslemSilindi, OlayTuru::CamYoluHesaplandi, OlayTuru::CamOpEklendi, OlayTuru::CamOpSilindi, OlayTuru::CamParcaOpEklendi, OlayTuru::CamParcaOpSilindi, OlayTuru::CamOpYoluHesaplandi, OlayTuru::AptOkundu, OlayTuru::AptYazildi, OlayTuru::FeatureEklendi, OlayTuru::FeatureGuncellendi, OlayTuru::FeatureSilindi, OlayTuru::FeatureHesaplandi, OlayTuru::TelEklendi, OlayTuru::YuzeyEklendi, OlayTuru::AnalizEklendi, OlayTuru::AnalizHesaplandi, OlayTuru::AnalizBayatladi, OlayTuru::AnalizSilindi, OlayTuru::KesitEklendi, OlayTuru::KesitDegisti, OlayTuru::DokumBasladi, OlayTuru::DokumBitti, OlayTuru::DolumBasladi, OlayTuru::DolumBitti, OlayTuru::HiyerarsiDegisti, OlayTuru::KomutCalisti}, [](const OlayKaydi&) { g_agacKirli = true; }); s_abone = true; } // Kaynak → FikoUi düğüm dönüşümü (dugumDonustur helper) — YALNIZ kirliyken. bool agacKuruldu = false; // bu kare ağaç yeniden kuruldu mu (seçim senkronunu zorlar) if (g_agacKirli) { agacKuruldu = true; s_agacKok.clear(); cekirdek.agac().topla(cekirdek, s_agacKok); s_harita.clear(); s_featureSonuc.clear(); agacUi.kokler.clear(); const u32 rollbackFeat = b.partD.rollbackNoktasi(); // rollback çubuğu satırını işaretle s_mKut = &b.malzemeKut; // malzeme rozeti kaynağı (yalnız kurulum boyunca) s_mAta = &b.malzemeAta; for (const AgacDugum& k : s_agacKok) { ui::AgacDugum kk; if (dugumDonustur(k, kk, cekirdek, sahne, s_harita, s_featureSonuc, filtre, rollbackFeat)) agacUi.kokler.push_back(std::move(kk)); } s_mKut = nullptr; s_mAta = nullptr; agacUi.yenile(); // düzleştirme cache'ini de geçersiz kıl (kokler yeniden kuruldu) g_agacKirli = false; } // ---- Sahne → ağaç hover: imleç-altı sketch geometrisini ağaçta vurgula ---- // PERF: eşleşmeyi TÜM s_harita'yı gezerek buluyordu (O(node) HER KARE, sketch düzenlerken // sürekli). (aktifSketch, sketchHover) + ağaç yapısı AYNIYSA önceki bulunan id'yi kullan (O(1)). if (aktifSketch != VARLIK_YOK && b.sketch.sketchHover != 0) { static Varlik s_sonHoverSketch = VARLIK_YOK; static u32 s_sonSketchHover = 0; static u64 s_sonDisHover = 0; if (agacKuruldu || aktifSketch != s_sonHoverSketch || b.sketch.sketchHover != s_sonSketchHover) { s_sonHoverSketch = aktifSketch; s_sonSketchHover = b.sketch.sketchHover; s_sonDisHover = 0; for (const auto& [id, d] : s_harita) if (d->camKimlik == aktifSketch && d->sketchOge == SketchOge::Geometri && d->sketchOgeKimlik == b.sketch.sketchHover) { s_sonDisHover = id; break; } } agacUi.disHover = s_sonDisHover; } // ---- Seçimi domain'den senkronla (tek yön: sahne/CAM → ağaç görünümü) ---- // PERF: bu senkron TÜM s_harita'yı gezer (O(node)) — büyük montajda boşta karede bile. // Sahne/CAM seçimi + ağaç yapısı AYNIYSA agacUi seçimi zaten güncel → tüm taramayı ATLA // (idle karede O(1)). İmza = seçili gövde kümesi (sırasız xor) + boyut + CAM op/parça. u64 secimImza = 1469598103934665603ull; for (u32 gk : sahne.seciliGovdeler()) secimImza ^= gk * 0x9E3779B97F4A7C15ull; secimImza += sahne.seciliGovdeler().size() * 0x100000001B3ull; secimImza ^= ((u64)(u32)camSis.seciliOp << 1) ^ ((u64)(u32)camSis.aktifParcaOp << 33); static u64 s_sonSecimImza = ~0ull; if (agacKuruldu) s_sonSecimImza = ~0ull; // ağaç yeniden kuruldu → s_harita id'leri yenilendi, zorla if (secimImza != s_sonSecimImza || b.agacKaydirGerek) { s_sonSecimImza = secimImza; std::unordered_set seciliSet; for (u32 gk : sahne.seciliGovdeler()) seciliSet.insert(gk); const auto govdeSecili = [&](u32 gk) { return gk != 0 && seciliSet.count(gk) != 0; }; // Seçili id'leri BİR vektöre topla → agacUi'ya TEK atama (secTopluAyarla; O(M) find yok). static std::vector s_seciliIdler; // kare-başı tekrar tahsis yerine yeniden kullan s_seciliIdler.clear(); u64 ilkSecili = 0; for (const auto& [id, d] : s_harita) { bool sec = false; if (d->varlik != VARLIK_YOK) { sec = govdeSecili(d->varlik); // CATIA senkron: feature'ın sonuç katısı 3D'de seçiliyse feature satırı highlight // (sonuç düğümü gizli). featureSonuc kurulumda hesaplandı → kare-başı RTTI YOK. if (!sec) { auto fit = s_featureSonuc.find(id); if (fit != s_featureSonuc.end() && fit->second) sec = govdeSecili(fit->second); } } else if (d->camTip == CAM_OP) sec = camSis.seciliOp == (int)d->camKimlik; else if (d->camTip == CAM_PARTOP) sec = camSis.aktifParcaOp == (int)d->camKimlik; if (sec) { s_seciliIdler.push_back(id); if (!ilkSecili) ilkSecili = id; } } agacUi.secTopluAyarla(s_seciliIdler); // K2: seçim değişti → seçili satırı görünür alana kaydır (bir kez). if (b.agacKaydirGerek) { if (ilkSecili) agacUi.kaydir(ilkSecili); b.agacKaydirGerek = false; } } // Şerit alt sınırı (ağaç başlamadan hemen önce) → şerit bandı = üst..bura. const bool seritUstunde = ImGui::IsMouseHoveringRect( seritMin, {seritMin.x + seritGen, ImGui::GetCursorScreenPos().y}); // ---- Sahne-içi (P2) zoom/pan/sığdır: main.cpp durumunu agacUi'ya ilet ---- agacUi.yaziOlcek = b.agacSahneOlcek; // wheel-zoom (overlay); panel modunda 1.0 if (b.agacSigdir) { agacUi.basaKaydir(); b.agacSahneOlcek = 1.0f; b.agacSigdir = false; } if (b.agacPanDelta != 0.0f) { agacUi.panla(b.agacPanDelta); b.agacPanDelta = 0.0f; } // ---- Çiz + olayları işle ------------------------------------------------- agacUi.filtre = (filtre[0] == ':') ? "" : filtre; // komut filtresi → alt-dize vurgusu yok const ui::AgacOlaylari ev = agacUi.ciz("##urunAgac"); g_agacKlavyeOdakta = ev.odakta; // global Delete işleyicisi bunu okur (ağaç odaktaysa dugumSil) auto kaynak = [&](u64 id) -> const AgacDugum* { auto it = s_harita.find(id); return it == s_harita.end() ? nullptr : it->second; }; // İmleç altındaki satır → 3B sahne vurgusu (eski davranış korunur). if (const AgacDugum* d = kaynak(ev.hoverId)) { if (d->varlik != VARLIK_YOK && sahne.bul(d->varlik)) sahne.vurgu = {d->varlik, 0}; // Ağaç → sahne: sketch alt-öğesi hover edilince overlay'de karşılığını vurgula // (yalnız aktif sketch — geo id'leri sketch-yerel, çapraz eşleşme olmasın). if (d->camKimlik == aktifSketch && aktifSketch != VARLIK_YOK) { if (d->sketchOge == SketchOge::Geometri) b.sketch.disHoverGeo = d->sketchOgeKimlik; else if (d->sketchOge == SketchOge::Kisit) b.sketch.disHoverKisit = d->sketchOgeKimlik; } } // Seçim: geometri → alt ağaç gövdeleri; CAM → seciliOp/aktifParcaOp. if (ev.secimDegisti) { const std::vector secimOnce = sahne.secililer(); // undo (SecimIslem) icin sahne.secimTemizle(); for (u64 id : ev.secili) { const AgacDugum* d = kaynak(id); if (!d) continue; if (d->camTip == CAM_OP || d->camTip == CAM_TAKIM || d->camTip == CAM_YOL) camSis.seciliOp = (int)d->camKimlik; else if (d->camTip == CAM_PARTOP || d->camTip == CAM_AXIS || d->camTip == CAM_STOK) camSis.aktifParcaOp = (int)d->camKimlik; else if (d->arketip == Arketip::Olcum && d->varlik == VARLIK_YOK) { // Ölçüm satırı seçilince referans aldığı gövdeler sahnede vurgulanır. if (Olcum* o = olcumBul(b.olcum, d->camKimlik)) { if (o->k1.govde && sahne.bul(o->k1.govde)) sahne.sec({o->k1.govde, 0}, true); if (o->k2.var && o->k2.govde && sahne.bul(o->k2.govde)) sahne.sec({o->k2.govde, 0}, true); } } else { std::vector hedef; dugumGovdeleriTopla(*d, sahne, hedef); sahne.secTopluEkle(hedef); // TEK olay + O(N) (eskiden N× sec → O(N²) + N olay = kasma) } } // PERF (büyük ağaç): ağaçtan seçilen satır ZATEN görünür → SecimDegisti abonesinin set // ettiği otomatik-kaydırmayı iptal et. Kaydırma; atalariAc (TÜM ağaç özyineli tarama) + // duzKirli_ yeniden-düzleştirme tetikliyordu → her tık/SAĞ-TIK'ta gereksiz O(N) iş // (right-click kasmasının kökü). Yalnız 3B sahneden gelen seçim kaydırır (satır ekran // dışı olabilir); o yol ev.secimDegisti'yi set etmez, bayrağı korur. b.agacKaydirGerek = false; // Bu seçim ÜRÜN AĞACINDAN yapıldı → transform gizmosu gövde-modunu bastırsın (gizmo // ağaç seçiminde seçime ZIPLAMASIN; yalnız 3D-sahneden seçimde belirir). sec/secTopluEkle // yukarıda false'a çekti; burada tree override. Kullanıcı: "ağaçtan seçince gizmoyu taşımayalım". sahne.secimAgactanAyarla(true); // Undo/redo (SecimIslem) — 3D viewport tıklaması secimTiklaKaydet ile aynı desen: // fark yoksa (ör. aynı satıra tekrar tık) yığın kirlenmez. std::vector secimSonra = sahne.secililer(); if (secimSonra != secimOnce) cekirdek.uygula(std::make_unique(sahne, secimOnce, std::move(secimSonra))); } if (ev.gorunurlukDegisti) if (const AgacDugum* d = kaynak(ev.gorunurlukId)) if (d->varlik != VARLIK_YOK) b.gorunurDegistir(d->varlik, ev.yeniGorunur); // Rollback çubuğu sürüklendi (Onshape click-drag): feature satırına bırakıldı → O feature'a // sar; hedefId 0 = tüm feature'ların altına bırakıldı → iptal. Adım butonları/menü ile aynı // rollbackAyarla (motor değişmez). Feature olmayan hedef sessizce yok sayılır. if (ev.rollbackTasindi) { if (ev.rollbackHedefId == 0) { b.partD.rollbackAyarla(0); g_agacKirli = true; } else if (const AgacDugum* d = kaynak(ev.rollbackHedefId)) { if (d->varlik != VARLIK_YOK && dynamic_cast(cekirdek.nesne(d->varlik))) { b.partD.rollbackAyarla(d->varlik); g_agacKirli = true; } } } // Çift-tık: CAM penceresi / analiz / sketch gir / sığdır (eski davranış). // Sanal malzeme satırında → malzeme penceresi (feature'ın çift-tık ile açılması gibi). if (ev.ciftTiklandi) if (const AgacDugum* d = kaynak(ev.ciftTikId)) agacCiftTikIsle(cekirdek, sahne, b, *d, s_paramDuzenle, s_paramAd, s_paramIfade, s_paramBirim); // ---- Sürükle-bırak: ağaçta yeniden düzenle (reparent + kardeş sırası) ---- // Yalnız gerçek ECS varlık düğümleri taşınır (CAM/ölçüm/sketch-öğe/sanal grup // hariç). Döngü koruması: bir düğüm kendi torununun içine/yanına konamaz. if (ev.surukleBirak && ev.surukleKaynak != ev.surukleHedef) { const AgacDugum* ks = kaynak(ev.surukleKaynak); const AgacDugum* hs = kaynak(ev.surukleHedef); // FEATURE→FEATURE sürükle = recompute SIRASI değiştir (featureler_ + ECS sira senkron). // Bağımlılık geçerliyse uygula+undo; geçersizse REDDET+uyarı. Generic ECS reparent atlanır. parca::Feature* kf = ks ? dynamic_cast(cekirdek.nesne(ks->varlik)) : nullptr; parca::Feature* hf = hs ? dynamic_cast(cekirdek.nesne(hs->varlik)) : nullptr; if (kf && hf && ks->varlik != hs->varlik) { const bool oncesine = ev.surukleKonum <= 0; // -1 önce · 0 içine · +1 sonra → içine≈önce std::vector yeni; if (b.partD.featureSiraOner(ks->varlik, hs->varlik, oncesine, yeni)) { std::vector eski = b.partD.featureSirasi(); b.partD.featureSirasiniAyarla(yeni); // canlı uygula (recompute + display senkron) cekirdek.uygula(std::make_unique(b.partD, std::move(eski), std::move(yeni))); g_agacKirli = true; ui::toastGoster("Feature sırası değişti", ui::ToastTip::Basari); } else ui::toastGoster("Geçersiz sıra (bağımlılık ihlali — üretici sonra kalamaz)", ui::ToastTip::Uyari); } else if (ks && hs && ks->varlik != VARLIK_YOK && ks->camTip == CAM_YOK && hs->varlik != VARLIK_YOK && hs->camTip == CAM_YOK) { Dunya& dny = cekirdek.dunya(); const Varlik K = ks->varlik, H = hs->varlik; Varlik hedefUst; if (ev.surukleKonum == 0) hedefUst = H; // içine → H yeni ebeveyn else { const HiyerarsiBilesen* hh = dny.bul(H); hedefUst = hh ? hh->ust : VARLIK_YOK; } // yanına → H'nin ebeveyni const bool dongu = hedefUst != VARLIK_YOK && (hedefUst == K || ataMi(dny, K, hedefUst)); if (!dongu) { auto kayitlar = tasiKayitlariKur(dny, K, H, hedefUst, ev.surukleKonum); if (!kayitlar.empty()) cekirdek.uygula(std::make_unique(std::move(kayitlar))); } } } // ---- Parametre düzenleme popup'ı (çift-tıkla açılır) ---- paramDuzenlePopupCiz(cekirdek, sahne, s_paramDuzenle, s_paramAd, s_paramIfade, s_paramBirim); // Hızlı eylemler (satır sağı): sığdır / yeniden adlandır / CAM pencere. if (ev.hizliEylem) if (const AgacDugum* d = kaynak(ev.hizliSatirId)) hizliEylemIsle(cekirdek, sahne, b, agacUi, *d, ev.hizliEylemId, ev.hizliSatirId); // Rename (bağlam menüsünden başlatılır) → DokumanNesnesi adı. if (ev.yenidenAdlandi) if (const AgacDugum* d = kaynak(ev.adId)) if (DokumanNesnesi* n = d->varlik != VARLIK_YOK ? cekirdek.nesne(d->varlik) : nullptr) { n->adAyarla(ev.yeniAd); g_agacKirli = true; // etiket değişti (olaysız) → ağacı yeniden kur } // ---- Bağlam menüsü (sağ-tık): CAM = mevcut menü; geometri = ad/sığdır/gizle. static u64 baglamId = 0; static u64 s_ogeSimgeDuzenle = 0; // öğe-simge editörü açık düğüm (0 = kapalı) static bool s_ogeSimgeAc = false; // bu kare editör popup'ını aç static char s_ogeSimgeBuf[260] = ""; u32 camSilOp = 0, camSilPop = 0; bool silIstendi = false; int gorunurEylem = 0; // 0 yok · 1 izole · 2 tümünü göster · 3 seçilileri gizle · 4 seçilileri göster int topluBastir = 0; // 0 yok · 1 bastır · 2 aç (çoklu seçim → ev.secili feature'larına) bool topluSil = false; // çoklu seçim → ev.secili tüm düğümlerini sil if (ev.baglamIstendi) { baglamId = ev.baglamId; ImGui::OpenPopup("##agacBaglam"); } if (ImGui::BeginPopup("##agacBaglam", ImGuiWindowFlags_AlwaysAutoResize)) { const AgacDugum* d = kaynak(baglamId); // Malzeme feature düğümü (gerçek ECS nesnesi): kendi menüsü. if (malzemeDugumuMu(cekirdek, static_cast(baglamId))) { const Varlik mv = static_cast(baglamId); auto* mn = dynamic_cast(cekirdek.nesne(mv)); const u32 gv2 = mn ? mn->govde() : VARLIK_YOK; const u32 mevcut = gv2 ? b.malzemeAta.govdeninMalzemesi(gv2) : 0; const auto uygula2 = [&](u32 yeni) { if (!gv2) return; const malzeme::MalzemeAtama once = b.malzemeAta; b.malzemeAta.govdeyeAta(gv2, yeni); cekirdek.uygula(std::make_unique( b.malzemeAta, once, b.malzemeAta, yeni ? "Malzeme Değiştir" : "Malzeme Kaldır")); malzemeDugumleriSenkron(cekirdek, sahne, b.malzemeKut, b.malzemeAta); g_agacKirli = true; }; ImGui::TextDisabled("🎨 Malzeme"); ImGui::Separator(); if (ImGui::BeginMenu("🔁 Değiştir")) { for (const malzeme::MalzemeTanim& m : b.malzemeKut.hepsi()) if (ImGui::MenuItem(m.ad.c_str(), nullptr, m.kimlik == mevcut)) uygula2(m.kimlik); ImGui::EndMenu(); } if (ImGui::MenuItem("⚙ Özellikleri Düzenle…")) malzemePencereAc(); ImGui::Separator(); if (ImGui::MenuItem("✖ Kaldır")) uygula2(0); } else if (d && d->camTip != CAM_YOK) { camDugumMenu(camSis, d->camTip, d->camKimlik, camSilOp, camSilPop, [](u32 id) { camOperasyonPencereAc(id); }, [](u32 id) { camParcaOpPencereAc(id); }); } else if (d && d->varlik != VARLIK_YOK) { geometriDugumBaglamMenu(cekirdek, b, sahne, agacUi, *d, baglamId, s_ogeSimgeDuzenle, s_ogeSimgeAc, s_ogeSimgeBuf, sizeof(s_ogeSimgeBuf), silIstendi, gorunurEylem, topluBastir, topluSil); } else if (d && d->sketchOge != SketchOge::Yok) { sketchOgeBaglamMenu(cekirdek, b, *d, aktifSketch); } else if (d && d->arketip == Arketip::Olcum && d->varlik == VARLIK_YOK) { olcumBaglamMenu(b, sahne, *d); } ImGui::EndPopup(); } // ---- Öğe-simge editörü (sağ-tık "🎨 Simge Değiştir") ---- ogeSimgeEditorCiz(cekirdek, b, s_ogeSimgeDuzenle, s_ogeSimgeAc, s_ogeSimgeBuf, sizeof(s_ogeSimgeBuf)); if (camSilOp) camSis.opSil(camSilOp); if (camSilPop) camSis.parcaOpSil(camSilPop); // Sil (obje/analiz/grup): grup ise çocuklarla birlikte. Popup çizimi bitince uygula // (kaynak cache s_agacKok bu kare geçerli; dugumSil g_agacKirli set → sonraki kare kurar). if (silIstendi) if (const AgacDugum* d = kaynak(baglamId)) dugumSil(*d, cekirdek, sahne, b); // Görünürlük toplu eylemleri. // 1 İzole: seçili düğüm alt-ağacı görünür, diğer TÜM gövdeler gizli (tersinir). // 2 Tümünü Göster. 3/4 Seçili öğeleri (ve alt gövdelerini) gizle/göster. if (gorunurEylem == 1 || gorunurEylem == 2) { std::vector tut; if (gorunurEylem == 1) if (const AgacDugum* d = kaynak(baglamId)) dugumGovdeleriTopla(*d, sahne, tut); std::function gez = [&](const AgacDugum& nn) { if (nn.varlik != VARLIK_YOK && sahne.bul(nn.varlik)) { const bool gor = gorunurEylem == 2 || std::find(tut.begin(), tut.end(), nn.varlik) != tut.end(); b.gorunurDegistir(nn.varlik, gor); } for (const auto& c : nn.cocuklar) gez(c); }; for (const auto& k : s_agacKok) gez(k); } else if (gorunurEylem == 3 || gorunurEylem == 4) { const bool gor = gorunurEylem == 4; for (u64 id : ev.secili) if (const AgacDugum* d = kaynak(id)) { std::vector govdeler; dugumGovdeleriTopla(*d, sahne, govdeler); for (u32 gv : govdeler) b.gorunurDegistir(gv, gor); } } // ---- Çoklu seçim toplu eylemleri: Suppress/Aç + Sil (ev.secili üzerinden) ---- // Bağlam menüsü ev.secili'yi (widget çoklu-seçim kümesi) taşımadığından burada uygulanır. if (topluBastir) { const bool bastir = topluBastir == 1; for (u64 id : ev.secili) if (const AgacDugum* d = kaynak(id); d && d->varlik != VARLIK_YOK) if (dynamic_cast(cekirdek.nesne(d->varlik))) cekirdek.uygula( std::make_unique(b.partD, d->varlik, bastir)); g_agacKirli = true; } // Toplu sil: context menü "N Öğeyi Sil" VEYA ağaç odaktayken Delete tuşu (ev.silIstendi). // Delete artık feature/grup/sketch/parametre/ölçüm TÜM tiplerini siler (eskiden global // seciliSil yalnız gövde siliyordu → çoğu düğümde çalışmıyordu) + çoklu seçim. if (topluSil || ev.silIstendi) { // Ata+torun ikisi de seçiliyse torun ata ile zaten silinir → dugumSil sahne.bul null // görüp atlar (çift-sil güvenli). Kaynak cache bu kare geçerli. for (u64 id : ev.secili) if (const AgacDugum* d = kaynak(id)) dugumSil(*d, cekirdek, sahne, b); } // Overlay tık-geçişi (CATIA): interaktif öğe (üst şerit VEYA gerçek satır) // üzerindeyse ağaç tüketir; boş alanda main.cpp arkadaki 3B sahneyi (kamera/ // pick) serbest bırakır. Bağlam menüsü açıkken de tüket (popup 3B'yi yemeli). // ---- Parametrik bağlantı çizgileri (Feature → Sketch/Parametre) ---- // Not: Gerçek koordinat alma için ağaç düğümünün ekran konumunu önce ölçmemiz gerekiyor. // Bu placeholder bir uygulamadır - gerçek UI'de ImGui::GetCursorScreenPos() kullanılmalı. return seritUstunde || ev.fareSatirUstunde || ImGui::IsPopupOpen("##agacBaglam"); } // Ürün Ağacı ayetleri kaydı (kullanıcı ayarları) void kaydetAgac(ayar::AyarSistemi& s, UygulamaBaglam& b) { using ayar::AyarDeger; using ayar::AyarTip; using ayar::dBool; using ayar::dF; using ayar::dMetin; // Ürün ağacının görünürlük ayarı - panel başlangıç durumu s.ayarKaydet({.kimlik = "agac.goster", .kategori = "Ürün Ağacı", .ad = "Ürün Ağacını göster", .aciklama = "Ana pencerenin solunda ürün ağacı paneli. Kapalı: ürün ağacı gizli.", .tip = AyarTip::Bool, .varsayilan = dBool(true), .al = [&b] { return dBool(b.panelAgac); }, .ata = [&b](const AyarDeger& d) { b.panelAgac = d.b; }}); // Ürün ağacı taşınabilirlik (çok pencetli mod) s.ayarKaydet({.kimlik = "agac.tasibilir", .kategori = "Ürün Ağacı", .ad = "Ağaç penceresini taşılabilir yap", .aciklama = "Ürün ağacı paneli ana pencerenin dışına taşınabilir (çok pencetli CAD).", .tip = AyarTip::Bool, .varsayilan = dBool(pencereDisariTasima()), .al = [] { return dBool(pencereDisariTasima()); }, .ata = [](const AyarDeger& d) { pencereDisariTasima() = d.b; }}); // Ürün ağacı filtre çubuğu görünürlüğü (filtre çubuğu varsayılan olarak gizli olabilir) s.ayarKaydet({.kimlik = "agac.filtre_goster", .kategori = "Ürün Ağacı", .ad = "Filtre çubuğunu göster", .aciklama = "Ürün ağacının üst şeridindeki filtre giriş kutusunu ve butonlarını göster/gizle.", .tip = AyarTip::Bool, .varsayilan = dBool(true), .al = [&b] { return dBool(b.panelAgac); }, // ağaç görünürken filtre de görünür .ata = [](const AyarDeger& d) { /* ayarlama başka yerden */ }}); // Ürün ağacı izleme (hover bilgi kartları) s.ayarKaydet({.kimlik = "agac.hover_bilgi", .kategori = "Ürün Ağacı", .ad = "Ağaçta hover bilgi kartı", .aciklama = "Ürün ağacındaki öğelere imleç bıraktığınızda B-Rep bilgileri gösterilsin.", .tip = AyarTip::Bool, .varsayilan = dBool(true), .al = [&b] { return dBool(b.hoverBilgiAcik); }, .ata = [&b](const AyarDeger& d) { b.hoverBilgiAcik = d.b; }}); // Ağaç girinti/dal bağlantı çizgileri s.ayarKaydet({.kimlik = "agac.girinti_cizgileri", .kategori = "Ürün Ağacı", .ad = "Girinti bağlantı çizgilerini göster", .aciklama = "Ağaçtaki üst-alt ilişkisini gösteren dikey/yatay girinti çizgileri (└ ├ tarzı).", .tip = AyarTip::Bool, .varsayilan = dBool(true), .al = [] { return dBool(g_agacGirintiCizgileri); }, .ata = [](const AyarDeger& d) { g_agacGirintiCizgileri = d.b; }}); // Kaydırma çubuğu konumu s.ayarKaydet({.kimlik = "agac.sol_kaydirma_cubugu", .kategori = "Ürün Ağacı", .ad = "Kaydırma çubuğunu solda göster", .aciklama = "Açık: CATIA tarzı sol kenar kaydırma çubuğu. Kapalı: standart sağ kenar.", .tip = AyarTip::Bool, .varsayilan = dBool(true), .al = [] { return dBool(g_agacSolKaydirma); }, .ata = [](const AyarDeger& d) { g_agacSolKaydirma = d.b; }}); // Satır yüksekliği (yoğunluk) s.ayarKaydet({.kimlik = "agac.satir_yukseklik", .kategori = "Ürün Ağacı", .ad = "Satır yüksekliği (piksel)", .aciklama = "Ağaç satırlarının yüksekliği. 0 = otomatik (yazı tipine göre); daha sık/geniş " "görünüm için elle ayarlanabilir.", .tip = AyarTip::F32, .varsayilan = dF(0.0), .min = 0, .maks = 40, .al = [] { return dF(g_agacSatirYukseklik); }, .ata = [](const AyarDeger& d) { g_agacSatirYukseklik = (f32)d.f; }}); // İnline yeniden adlandırma s.ayarKaydet({.kimlik = "agac.yeniden_adlandirma", .kategori = "Ürün Ağacı", .ad = "Yeniden adlandırmaya izin ver", .aciklama = "Kapalı: ağaçtaki düğümler bağlam menüsünden de dahil hiçbir yoldan " "yeniden adlandırılamaz (kazara ad değişimini önler).", .tip = AyarTip::Bool, .varsayilan = dBool(true), .al = [] { return dBool(g_agacYenidenAdlandirma); }, .ata = [](const AyarDeger& d) { g_agacYenidenAdlandirma = d.b; }}); // Görünürlük (göz) sütunu s.ayarKaydet({.kimlik = "agac.gorunurluk_sutunu", .kategori = "Ürün Ağacı", .ad = "Görünürlük (👁) sütununu göster", .aciklama = "Kapalı: satır başındaki göz simgesi gizlenir, ağaç salt-okuma gibi görünür " "(görünürlük değiştirme yine 3B sahne/bağlam menüsünden yapılabilir).", .tip = AyarTip::Bool, .varsayilan = dBool(true), .al = [] { return dBool(g_agacGorunurlukSutunu); }, .ata = [](const AyarDeger& d) { g_agacGorunurlukSutunu = d.b; }}); // İlk açılışta tüm dallar açık mı s.ayarKaydet({.kimlik = "agac.varsayilan_hepsi_acik", .kategori = "Ürün Ağacı", .ad = "İlk açılışta tüm dalları aç", .aciklama = "Açık: belge yüklenince ürün ağacı tümüyle açık başlar. Kapalı (varsayılan): " "her düğümün kendi varsayılan katlı/açık durumu kullanılır.", .tip = AyarTip::Bool, .varsayilan = dBool(false), .al = [] { return dBool(g_agacVarsayilanHepsiAcik); }, .ata = [](const AyarDeger& d) { g_agacVarsayilanHepsiAcik = d.b; }}); } } // namespace fiko