#include #include // _dup2/_fileno — senaryo log stderr→stdout birleştirme #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cam_paneli.hpp" #include "cam_operasyon_penceresi.hpp" #include "cam_simulasyon_penceresi.hpp" #include "acilis_splash.hpp" #include "belge.hpp" #include "belge_ciz.hpp" #include "surec_kopru.hpp" #include "orkestra_kule.hpp" #include "istakip_servis.hpp" #include "belge_yonetici.hpp" #include "cam_parca_op_penceresi.hpp" #include "cam_takim_penceresi.hpp" #include "cam_takim_ekseni_penceresi.hpp" #include "cam_urun_agaci.hpp" #include "feature_penceresi.hpp" #include "fiko/ui/web_skin.hpp" // webSkinGridCiz — merkezî pencere ızgarası (Ayarlar → Pencere) #include "akilli_secim.hpp" #include "akilli_oneri_cip.hpp" #include "makro_oneri.hpp" #include "makro_oneri_cip.hpp" #include "kafa_penceresi.hpp" #include "lisans_kimlikler.hpp" // kalıcı komut kimliği (lisans/fiyatlandırma) #include "lisans_yukle.hpp" // imzalı .fkl yükleme + lisans durumu #include "lisans_uzak.hpp" // uzak politika pompa (kill-switch) #include "lisans_canli.hpp" // uzakCanliDurdur (kapanışta thread'i durdur) #include "lisans_uzaktan.hpp" // UZAKTAN KOMUT + lisans bileti (/socket.io/) #include "arac_kayitlari.hpp" // araç kipi (tek araç ana pencere) #include "fiko/cekirdek/arac_kipi.hpp" #ifdef FIKO_URETICI_UI #include "lisans_uretici_penceresi.hpp" // 🏭 üretici arayüzü — YALNIZ üretici derlemesinde #include "lisans_yonetim.hpp" // 🛠 yönetim konsolu (uzaktan lisans/komut) — üretici only #endif #ifdef FIKO_CEF #include "yardim_cef.hpp" // yardimCefPompa #include "yardim_penceresi.hpp" // yardimPenceresiCiz (gömülü CEF paneli) #endif #include "referans_penceresi.hpp" #include "gsd_penceresi.hpp" #include "hizli3d_penceresi.hpp" #include "workbench_secici.hpp" #include "analiz_penceresi.hpp" #include "analiz_urun_agaci.hpp" #include "dokum_penceresi.hpp" #include "fiko/dokum/dokum_simulasyonu.hpp" #include "fiko/dokum/sph_gpu.hpp" // senaryo: GPU SPH dispatch doğrulaması #include "fiko/analiz/analiz_sistemi.hpp" #include "fiko/betik/makro_motor.hpp" #include "fiko/parca/part_design_sistemi.hpp" #include "fiko/gsd/gsd_sistemi.hpp" #include "fiko/cam/apt.hpp" #include "fiko/cam/cam_islemleri.hpp" #include "fiko/cam/cam_secim.hpp" #include "fiko/cam/cam_sistemi.hpp" #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/cekirdek/guncelleme.hpp" #include "guncelleme_kontrol.hpp" #include "guncelleme_indir.hpp" #include "windows_surum.hpp" // CATIA Çalışma Alanı: gerçek Win10/11 build tespiti #include "sanal_masaustu.hpp" // CATIA Çalışma Alanı: ComOturum + sanal masaüstü sorgusu #include "catia_kesif.hpp" // CATIA Çalışma Alanı: çoklu CNEXT.exe keşfi #include "calisma_alani_plan.hpp" // CATIA Çalışma Alanı: plan dosyası yaz/oku/karşılaştır #include "fiko/cekirdek/surum.hpp" #include "fiko/cekirdek/fiko_kisayol.hpp" #include "fiko/cekirdek/fiko_son_dosyalar.hpp" #include "fiko/cekirdek/urun_agaci.hpp" #include "fiko/girdi/baglam_politika.hpp" #include "fiko/girdi/dokunma_jest.hpp" #include "fiko/girdi/fare_jest.hpp" #include "fiko/girdi/hover_probu.hpp" #include "fiko/girdi/nav_kip.hpp" #include "fiko/eklenti/eklenti_yonetici.hpp" #include "fiko/core/gunluk.hpp" #include "fiko/core/kare_istek.hpp" #include "fiko/platform/pencere.hpp" #include "fiko/platform/sistem.hpp" #include "fiko/render/boru_hatti.hpp" #include "fiko/render/orbit_kamera.hpp" #include "fiko/render/referans_olcek.hpp" #include "fiko/render/secim_gpu.hpp" #include "fiko/render/vertex.hpp" #include "fiko/render/vulkan_baglam.hpp" #include "fiko/sahne/kutuk_hesap.hpp" #include "fiko/sahne/alan_secim.hpp" #include "fiko/sahne/olcum_sistemi.hpp" #include "fiko/geometri/kalite.hpp" // [ayar] perf logu: sapmaKuantala/usdYuzEsigi/filletDenemeMs #include "fiko/sahne/sahne.hpp" #include "fiko/render/context_pick_manager.hpp" #include "context_pick_ui.hpp" #include "fiko/sahne/sahne_islem.hpp" #include "fiko/sahne/sahne_ref_secim.hpp" #include "fiko/sahne/secim_sistemi.hpp" #include "fiko/sahne/sketch_nesne.hpp" #include "fiko/sketch/snap.hpp" #include "fiko/ui/fikoui.hpp" #include "fiko/ui/imgui_katman.hpp" #include "fiko/ui/pencere_cerceve.hpp" // ui::Pencere — merkezi pencere iskeleti (İşlemler) #include "fiko/ui/simge.hpp" // arka plan görseli (bulut+logo) GPU dokusu #include "imgui.h" #include "imgui_internal.h" #include "baglam_hud.hpp" #include "arama_menu.hpp" #include "baglam_menu.hpp" #include "durum_cubugu.hpp" #include "hover_karti.hpp" #include "kesit_gizmo.hpp" #include "navigasyon_ghost.hpp" #include "olcum_penceresi.hpp" #include "pencere_util.hpp" #include "perf_hud.hpp" #include "feature_reduzenle.hpp" #include "push_pull.hpp" #include "gsd_gizmo.hpp" #include "sketch_itcek.hpp" #include "kenar_fillet.hpp" #include "kenar_gizmo.hpp" #include "yuz_tasi_gizmo.hpp" #include "yuz_ghost.hpp" // gizmosuz async op (Yüz Kaldır) viewport ghost + ✕ iptal #include "pusula.hpp" #include "fark_stil.hpp" // uiStiliUygula() — global UI ince ayarları #include "fark_tur.hpp" #include "sahne_overlay.hpp" #include "vizyon_kupu.hpp" #include "secim_yigini.hpp" #include "uygulama_baglam.hpp" #include "ui_ortak.hpp" #include "ayar_kalici.hpp" #include "ayar_kayitlari.hpp" #include "ayarlar_penceresi.hpp" #include "kontrol_merkezi.hpp" #include "fiko/ayar/ayar_sistemi.hpp" #include "dosya_islemleri.hpp" #include "oto_kayit.hpp" // otomatik kayıt + çökme kurtarma #include "pafta_ciz.hpp" // teknik resim modu çizimi/etkileşimi #include "pmi_ciz.hpp" // 3B PMI/GD&T overlay #include "fiko/sahne/pmi_nesne.hpp" // pmiEkle/PmiSistemi (DM niyet önerileri) #include "pafta_ui.hpp" // teknik resim modu durumu (PaftaUIDurum) #include "fiko/sahne/pafta_nesne.hpp" // paftaVerisi / PaftaNesnesi #include "fiko_panel.hpp" #include "surec_yonetici_panel.hpp" // FikoSurecYoneticiPanel (merkezi süreç yöneticisi) #include "isik_durum.hpp" #include "isik_penceresi.hpp" #include "ozellikler_paneli.hpp" #include "sketch_ui.hpp" #include "kinematik_agac.hpp" #include "kinematik_penceresi.hpp" #include "fiko/montaj/kisit.hpp" // Montaj Kısıtları (seçim → mafsal köprüsü) #include "fiko/kinematik/mekanizma.hpp" // Mekanizma::uzuvEkle/mafsalEkle #include "urun_agaci_paneli.hpp" #include "yardimci_penceler.hpp" #include "belge_penceresi.hpp" namespace fiko { const char* g_sonAdim = "baslangic"; // extern tanim (header) std::atomic* g_gozcuKalp = nullptr; // gözcü kalp atışı (çizilen kare sayacı) int& gozcuSaniye() { static int sn = 5; // 5 sn kare yoksa donmuş say (ölçüldü: normal kare < 100 ms) return sn; } } // namespace fiko namespace { using namespace fiko; // Canlı önizleme sürükleme kimliği (kalıcı değil) — hem setup hem frame kullanır. constexpr u32 SURUKLE_CANLI = 0xFFFFFFFFu; // ---- Bağlam motoru → sağ-tık halkası köprüsü (Dalga 3) ---- // Motor sunumdan bağımsız BaglamOnerisi üretir; araç halkası girdi::MenuOgeSecim bekler // (yapısal olarak birebir). Bu köprü kaynak tampondan çevirir; simge işaretçileri KAYNAK // vektöre bakar → kaynak (sonMotorOneri_) menü ömrü boyunca yaşamalı (çağıran garanti eder). girdi::MenuOgeSecim baglamOneriCevir(const baglam::BaglamOnerisi& b) { girdi::MenuOgeSecim m; m.komutKimlik = b.komutKimlik; // std::string (sahiplenir) m.etiket = b.etiket; // std::string (sahiplenir) m.simge = b.simge.c_str(); // STABİL: b kaynak vektörde yaşar (çerçeve boyu) m.etkin = b.etkin; m.yon = b.yon; m.cocuklar.reserve(b.cocuklar.size()); for (const auto& c : b.cocuklar) m.cocuklar.push_back(baglamOneriCevir(c)); return m; } void baglamPolitikaYap(const std::vector& kaynak, girdi::PolitikaKarar& out) { out = girdi::PolitikaKarar{}; if (kaynak.empty()) return; out.menu = girdi::MenuTur::AracHalkasi; out.radyal = true; // motor önerileri nested radyal halka (yaprak + ara-öğe) out.ogeler.reserve(kaynak.size()); for (const auto& b : kaynak) out.ogeler.push_back(baglamOneriCevir(b)); } // Motorun ZENGİN kapsadığı bağlamlar (Dalga 3 kapsamı: gövde/boş/yüz/kenar/vertex). // Datum/GSD/CAM/ölçüm/sketch bağlamları Dalga 5'e kadar mevcut BaglamPolitika'ya düşer // (REGRESYON YOK). Bu kategoriler dışında motor sonucu KULLANILMAZ. bool baglamMotorKapsar(baglam::HedefKategori k) { using K = baglam::HedefKategori; switch (k) { case K::Bos: case K::Govde: case K::Vertex: case K::YuzDuz: case K::YuzSilindir: case K::YuzKonik: case K::YuzKure: case K::YuzEgri: case K::KenarCizgi: case K::KenarYay: case K::KenarCember: case K::KenarSpline: return true; default: return false; } } // ---- 3D görünüm arka planı: bulut fotoğrafı + ortada FikoYT logosu (web'deki // fikoyt-arka-1.webp'in birebiri, tek birleşik görsel). Görsel GPU dokusuna bir // kez (ilk karede) yüklenir, ömrü uygulama boyunca; her kare tek AddImage çizimi // (allocation yok). Görsel yoksa güvenli koyu gradyana düşer. ImGui pencere // drawlist'ine, offscreen sahne dokusundan (ImGui::Image) ÖNCE çizilir → sahne // nesneleri (saydam-temiz viewport sayesinde) arka planın önünde kalır. constexpr float kArkaGorselEn = 1920.0f; // kaynak görsel doğal en (cover için) constexpr float kArkaGorselBoy = 1200.0f; // kaynak görsel doğal boy void arkaPlanCiz(ImDrawList* dl, ImVec2 sol, ImVec2 boyut) { static ImTextureID s_doku = 0; static bool s_denendi = false; if (!s_denendi) { s_denendi = true; // tek deneme; başarısızsa gradyana düşülür, tekrar yüklenmez s_doku = ui::kitaplik::yukle("arka3d", sistem::calistirilabilirDizin() + "resimler\\fikoyt-arka.png"); } const ImVec2 sagAlt{sol.x + boyut.x, sol.y + boyut.y}; if (!s_doku) { // Fallback: web brightness(60%) hissini veren koyu dikey gradyan. const ImU32 ust = IM_COL32(28, 30, 40, 255), alt = IM_COL32(12, 13, 18, 255); dl->AddRectFilledMultiColor(sol, sagAlt, ust, ust, alt, alt); return; } // Cover: en-boyu koruyarak viewport'u tamamen kapla, taşan kenarı kırp (UV). const float hedefOran = boyut.x / std::max(boyut.y, 1.0f); const float gorselOran = kArkaGorselEn / kArkaGorselBoy; ImVec2 uv0{0, 0}, uv1{1, 1}; if (hedefOran > gorselOran) { // pencere daha geniş → dikeyden kırp const float k = gorselOran / hedefOran; uv0.y = (1.0f - k) * 0.5f; uv1.y = 1.0f - uv0.y; } else { // pencere daha dar → yataydan kırp const float k = hedefOran / gorselOran; uv0.x = (1.0f - k) * 0.5f; uv1.x = 1.0f - uv0.x; } // Web efekt yığını = SADECE `filter: brightness(60%)` (overlay/vignette/blur YOK // — fiko-3d-deneme/.component.scss:26; fikoyt-ui varyantı yorumda). CSS brightness // sRGB uzayında çarpar; doku sRGB örneklenip lineerleştiğinden ImGui tint LİNEER // uzayda çarpar. sRGB takas zincirinde eşdeğer: t = 0.6^2.2 ≈ 0.323 → 82/255. dl->AddImage(s_doku, sol, sagAlt, uv0, uv1, IM_COL32(82, 82, 82, 255)); } // Itme push-constant struct'ları + TUMU/floatBits → belge_ciz.cpp (MDI Dalga 2). struct FareDurum { bool sol = false, orta = false, sag = false; f32 sonX = 0, sonY = 0; f32 solBasX = 0, solBasY = 0, ortaBasX = 0, ortaBasY = 0; f32 sagBasX = 0, sagBasY = 0; std::chrono::steady_clock::time_point solBasZaman, ortaBasZaman, sagBasZaman; bool uzunBasIslendi = false; bool kameraKullandi = false; // bu sürüklemede kamera hareketi yapıldı mı (seçim/merkez baskıla) int kip = -1; // aktif kamera kipi: -1 yok, 0 döndür, 1 zoom, 2 pan (cache) girdi::NavKipTakip navKip; // merkezi kip durum makinesi (kip alanı bunun int karşılığı) }; // ============================================================================ // PencereOrtami — MDI Alt-proje 3 (docs mdi_blueprint / fikatia-mdi-alt3-plani): // bir PENCERENİN tüm per-window UI durumu tek nesnede. calistir()'daki dev döngü // gövdesi bare local'ları REFERANS-ALIAS ile bu nesneden alır (hizliDurum/sketchDurum // deseninin aynısı) → gövde BİREBİR kalır, davranış değişmez. Stage 2'de belge+bglm+ // yardımcılar da buraya girer; Stage 3-4'te her belge penceresi kendi PencereOrtami'siyle // aynı UI'yi (menü/toolbar/ağaç/panel) çalıştırır. Değer üye = pencere-başına sahiplik. // ============================================================================ struct PencereOrtami { // Belge: bu pencerenin dökümanı (Cekirdek+Sahne+kamera+tüm sistemler). Pencere-başına // sahiplik — 2. belge penceresi kendi Belge'siyle. unique_ptr: PencereOrtami erken // default-construct edilir, belge shaderDizin hazır olunca kurulur (belgeKur). std::unique_ptr belgePtr; // bglm: bu pencerenin UygulamaBaglam'ı (alt sistem + UI-state + helper referansları). // unique_ptr: UygulamaBaglam tüm-referans üyeli (default ctor yok) → belgeKur'da kurulur. // kareGovdesi/belgeKur helper'lara bunun üzerinden erişir (per-window doğru bağlanma). std::unique_ptr bglmPtr; // Bu pencerenin ImGui katmanı (context+backend). Ana pencerede calistir'daki arayuz'a // işaret eder; ek pencereler kendi ImGuiKatman'ını sahiplenir (Stage: çok-pencere). ImGuiKatman* imguiPtr = nullptr; // Komut paleti (Ctrl+Space): universal (komut + sahne nesnesi) tek fuzzy aramada. // paletKayit komut kaynagini besler; universalArama iki kaynagi (Komut/Nesne) toplar. fiko::KomutKaydi paletKayit; fiko::ui::FikoUniversalArama universalArama; fiko::KomutPaleti komutPalet; SistemMetrikToplayici metrikToplayici; // Kontrol Merkezi "Performans" panosu (CPU/GPU/RAM) HizliUIDurum hizliDurum; bool pickFiltreYedekVar = false; bool pickYedYuz = false, pickYedKenar = false, pickYedNokta = false, pickYedGovde = false; PusulaDurum pusula; PushPullDurum pushPull; // CATIA Push/Pull doğrudan yüz düzenleme modu GsdGizmoDurum gsdGizmo; // GSD Yüzey offset canlı sürükleme gizmosu (Dalga C) bool izoleAktif = false; // Isolation (Alt+H): seçili gövde yalıtıldı mı std::vector> izoleYedek; // yalıtım öncesi görünürlük (geri al için) KenarFilletDurum kenarFillet; // Doğrudan Modelleme: kenar fillet/pah canlı gizmosu SketchItCekDurum sketchItCek; // Doğrudan Modelleme: seçili sketch profilini tut-çek Pad/Cep FeatureReDuzenleDurum featureReDuzenle; // DM: fillet/chamfer yüzüne tıkla-sürükle → feature güncelle YuzTasiDurum yuzTasi; // DM: Move Face 3-eksen (X/Y/Z) sürükle tutamacı (yuz_tasi feature) KenarGizmoDurum kenarGizmo; // DM: kenar seçilince ⟀ eğ yayı + ⇱ uzat oku (kenar_gizmo.hpp) YuzGhostDurum yuzGhost; // Gizmosuz async op (Yüz Kaldır) viewport "işleniyor" ghost'u + ✕ iptal AramaMenuDurum dmAramaMenu; // Doğrudan Modelleme: yüze sağ-tık arama kutulu bağlam menüsü bool bosSagTikArama = false; // BOŞ yere sağ-tık → evrensel arama paleti aç (jest set eder, ana döngü tüketir) KesitGizmoDurum kesitGizmo; SecimYigini secimYigini; // CATIA "Other Selection" — Alt+tık aday yığını bool hizliGorunumAcik = false; IsikDurum isikDurum; bool panelIsik = false, isikUstte = true; bool panelAgac = true, panelOzellik = false, panelOlay = false; bool agacIcinde = true; bool agacOdakta = false; // sahne-içi ağaç (P2): odak ağaçta mı — zoom/pan ağaca gider, model kilitli f32 agacSahneOlcek = 1.0f; // sahne-içi ağaç yazı ölçeği (0.6–2.5; wheel ile) f32 agacPanDelta = 0.0f; // sahne-içi ağaç bu kare pan deltası (panel tüketir) bool agacSigdir = false; // sahne-içi ağaç "Sığdır" isteği (panel tüketir) bool ozellikAcikOnceki = false; bool agacKaydirGerek = false; bool agacUstte = true, ozellikUstte = true, olayUstte = true, islerUstte = true, olcumUstte = true, camUstte = true; bool panelIsler = true; bool panelMakro = false, panelEklenti = false; SketchUIDurum sketchDurum; PaftaUIDurum paftaDurum; PmiUIDurum pmiDurum; bool pah3DAcik = false, pah3DYuvarlat = false; char pah3DGiris[32] = "1.0"; size_t oncekiIsSayi = 0; u32 olcumSurukle = 0; int olcumSurukleMod = 0; DugumPusula ozellikEski; std::string durumMesaj; f32 durumMesajBitis = 0, simdikiZaman = 0; f32 sonEnBoy = 16.0f / 9.0f; f32 sonG = 1280, sonY_ = 720; uint64_t vpDoku = 0; VkImageView vpKayitliGorunum = VK_NULL_HANDLE; u32 istenenVpG = 1280, istenenVpY = 720; f32 vpRectMinX = 0, vpRectMinY = 0; bool vpHover = false; bool dockKuruldu = false; bool hoverBilgiAcik = true; bool hoverHudAcik = false; // bağlam HUD (imleç-yanı "ne + ne yapabilirsin"); opsiyonel FareDurum fare; // Dokunmatik/kalem jest durumu (fare yolundan BAĞIMSIZ; bkz. dokunma_jest.hpp). girdi::DokunmaJestSistemi dokunma; AkilliOneriDurum akilliOneri; // akıllı seçim öneri çipi (viewport altı) MakroOneriMotoru makroOneri; // tekrar yakala → makro öner (viewport üstü) // Makro adı sorma (çip kabulünde küçük kutu — Boyut penceresi kalıbı; Esc/kapat da // KAYDEDER: üretilen Lua kaybolmasın, varsayılan ad yeter). bool makroAdAcik = false; bool makroAdIlk = false; // ilk kare: konum + odak char makroAdGiris[64] = {}; std::string makroAdLua; // bekleyen üretilmiş Lua içerik // Parmakla seçim/menü sentetik tık aşaması: 2 = bu kare down yay, 1 = bu kare up yay, // 0 = boşta. down/up AYRI karelerde olmalı (ImGui tık kenarı kare geçişinde doğar). int dokunmaTikAsama = 0; int dokunmaNavKip = -1; // dokunmatik nav kipi (NavKipDegisti yayını için; -1 yok) // Parmakla tutamaç (R/C gizmo) sürükleme: 0=yok, 1=aday (parmak tutamaç üstünde, // henüz kaymadı), 2=aktif (sentetik sol tuş BASILI, tutamaç sürülüyor). int dokunmaIsaretci = 0; ImGuiMouseButton dokunmaTikDugme = ImGuiMouseButton_Left; // dokunuş=sol, uzun bas=sağ }; // tipAdi / tipSimge / olayRengi → ui_ortak.hpp (fiko namespace; using ile erişilir). // "Üstte tut" yardımcıları pencere_util.hpp'de (CAM paneli ile paylaşılır). // dosyaSec / aptDosyaSec → dosya_islemleri.hpp (fiko namespace; using ile erişilir). // ============================================================================ // FikoBelgePenceresi — OOP Dalga 1 (fikatia-oop-mimari-operasyonu / mdi-alt3): // bir belge penceresinin TAMAMI (kendi Win32 penceresi + Vulkan bağlamı + ImGui // katmanı + Belge + tüm UI durumu + komut/wb/olay kaydı) tek SINIF örneğinde. // Eski calistir() god-function'ının per-window durumu ve gövdesi buraya taşındı; // böylece "Yeni Belge" = yeni FikoBelgePenceresi örneği (gerçek ayrı pencere). // // TASARIM (member-alias trick): kur()/kareCiz() metod GÖVDELERİ eski calistir // kodunun BİREBİR aynısı — yalnız kapsayan scope sınıf oldu. Setup-yerel yardımcı // nesneler/lambda'lar ÜYE (jestSis_/g_araclar_/hatlar_/... ve helper lambda'lar) → // komut lambda'ları `[&]` ile `this`'i yakalar; nesne pencere ömrü yaşadığı için // dangling YOKTUR (transient-lambda yaklaşımının çözemediği sorun burada çözülür). // Ana pencere de bir örnektir; ek pencereler kendi örneğini alır (çok-pencere). // ============================================================================ class FikoBelgePenceresi { public: // Ana pencere (paylasilan=nullptr): kendi VulkanBaglam+BoruHatlari'nı yaratır+sahiplenir // (uygulama-global aygıt + ana yüzey). Ek pencere (paylasilan!=nullptr): AYNI VulkanBaglam'ı // ve BoruHatlari'nı PAYLAŞIR (ImGuiKatman global efekt/simge altyapısı tek aygıta bağlı → // ayrı aygıt ÇÖKME olur) + kendi PencereYuzeyi/ViewportHedef'ini yaratır (render retarget). FikoBelgePenceresi(int otoTestKare, bool senaryoDokum, bool anaMi, VulkanBaglam* paylasilanVulkan, BoruHatlari* paylasilanHatlar) : otoTestKare_(otoTestKare), senaryoDokum_(senaryoDokum), anaMi_(anaMi), pencere_(pencereAyarKur(anaMi)), vulkanSahip_(paylasilanVulkan ? nullptr : std::make_unique(pencere_)), vulkan_(paylasilanVulkan ? *paylasilanVulkan : *vulkanSahip_), arayuz_(pencere_, vulkan_), shaderDizin_(sistem::calistirilabilirDizin() + "shader\\"), olayLogYol_(sistem::calistirilabilirDizin() + "fikoyt-olaylar.fikolog"), hatlarSahip_(paylasilanHatlar ? nullptr : std::make_unique(vulkan_, shaderDizin_)), hatlar_(paylasilanHatlar ? *paylasilanHatlar : *hatlarSahip_) { // Ek pencere: kendi sunum yüzeyi + offscreen 3D hedefi (paylaşılan aygıta bağlı). if (!anaMi_) { yuzeySahip_ = std::make_unique(vulkan_.aygitNesne(), pencere_); vpSahip_ = std::make_unique(vulkan_); } } // Paylaşımlı kaynaklara erişim (ek pencere yaratımı için — ana pencereden alınır). VulkanBaglam& vulkanBaglam() { return vulkan_; } BoruHatlari& boruHatlari() { return hatlar_; } // kur(): eski setup (komut/wb/olay kaydı + helper'lar). Bir kez çağrılır. void kur(); // baslat(): kare döngüsü öncesi tek-sefer init (zaman/senaryo durumu). void baslat(); // kareCalistir(): bir kare (zaman + kareCiz + senaryo tail). false = bu pencere // çıkmak istiyor (oto-test bitti / aygıt kayıp / senaryo tamam / pencere kapandı). bool kareCalistir(); // kareCiz(): eski kareGovdesi (bir kare çizim). return false = kare atlandı. bool kareCiz(); bool kareCizIcinde_ = false; // modal-loop pencere.kareCiz callback re-entrancy guard'ı // Aygıt idle bekle (kapanış öncesi kaynak yıkımı güvenli olsun diye). void bekle() { vulkan_.bekle(); } bool anaMi() const { return anaMi_; } bool kapatildi() const { return pencere_.kapatildi(); } // Komut satırından / dosya ilişkilendirmesinden gelen yolu aç. Explorer sürükle-bırak // ile AYNI yolu kullanır (lisans hakkı kontrolü + uzantıya göre içe aktarıcı seçimi // orada yaşar) → tek doğru yol, ikinci bir kural kopyası yok. void dosyaYoluAc(const std::string& yol) { if (!yol.empty() && pencere_.dosyaBirakildi) pencere_.dosyaBirakildi(yol); } private: static PencereAyar pencereAyarKur(bool anaMi) { PencereAyar ayar; // Sürüm tek kaynaktan (surum.hpp). SURUM_METIN ASCII → karakter-karakter genişlet. static const std::wstring anaBaslik = L"FikoYT 3d Cad " + std::wstring(SURUM_METIN, SURUM_METIN + std::char_traits::length(SURUM_METIN)); ayar.baslik = anaMi ? anaBaslik : L"FikoYT — Yeni Belge"; // Araç kipi komut satırından/ortamdan isteniyorsa pencere BAŞTAN dar açılır (araç // yardımcı bir alet; 1600×900 ona göre devasa). Boyutu sonradan değiştirmek // WM_SIZE/swapchain yarışı demek — bu yüzden burada, yaratılmadan önce. // Lisanstan otomatik açılan araç kipi bu noktada henüz bilinemez (lisans sonra // yüklenir) → o yol varsayılan boyutla açılır, kullanıcı pencereyi ayarlar. const std::string istenen = anaMi ? AracSistemi::komutSatirindanArac(aracKomutSatiri()) : std::string(); if (!istenen.empty()) { // Araçlar kayıtlı olmalı (kısa ad → kimlik çözümü). Lisans kapısı kurulur ama // burada DEĞERLENDİRİLMEZ (lambda sonra çağrılır) → lisans henüz yüklenmemişken // çağırmak güvenli. aracKayitlariKur(); const std::string kimlik = aracSistemi().cozumle(istenen); // Araç BAŞINA hatırlanan geometri; yoksa genel varsayılan ölçü. const AracPencereGeo geo = kimlik.empty() ? AracPencereGeo{} : aracPencereGeoOku(kimlik); ayar.genislik = (u32)(geo.boyutGecerli ? geo.genislik : aracPencereGenislik()); ayar.yukseklik = (u32)(geo.boyutGecerli ? geo.yukseklik : aracPencereYukseklik()); if (geo.konumGecerli) { // ekran dışı kayıtlar aracPencereGeoOku'da elenir ayar.x = geo.x; ayar.y = geo.y; } // Sahip pencere (kafaAyriPencerdeAc ile başlatıldıysa): Win32 owned-window — // bu süreç kendi ana penceresidir (anaPencere=true kalır, kapanışı bağımsız), // yalnız z-order/minimize ilişkisi çağıran pencereye bağlanır. void* sahip = nullptr; if (komutSatirindanSahip(aracKomutSatiri(), sahip)) ayar.sahipTutamac = sahip; } return ayar; } // ---- CLI bayrakları ---- int otoTestKare_ = 0; bool senaryoDokum_ = false; bool anaMi_ = true; // ---- Pencere altyapısı ---- // pencere_ + arayuz_ HER örnekte kendine ait. VulkanBaglam+BoruHatlari yalnız ANA // pencerede yaratılır (uygulama-global aygıt); ek pencereler REFERANSLA paylaşır. Pencere pencere_; std::unique_ptr vulkanSahip_; // yalnız ana pencerede dolu VulkanBaglam& vulkan_; // her örnek: paylaşılan/sahip olunan ImGuiKatman arayuz_; // pencere-başına ImGui context (multi-context) const std::string shaderDizin_; const std::string olayLogYol_; const std::string makroDizin_ = sistem::calistirilabilirDizin() + "makrolar"; // ---- Render altyapısı ---- std::unique_ptr hatlarSahip_; // yalnız ana pencerede dolu BoruHatlari& hatlar_; // paylaşılan (device-global pipeline'lar) // Ek pencere: kendi sunum yüzeyi + offscreen 3D hedefi (ana pencere vulkan_ üzerinden). std::unique_ptr yuzeySahip_; // yalnız ek pencerede dolu std::unique_ptr vpSahip_; // yalnız ek pencerede dolu CizimOnbellek anaCizim_; BelgeYonetici belgeYon_; // ek belgeler (senaryo smoke; MDI Dalga 2 legacy) // ---- Per-window UI durumu + Belge + bglm (kur()'da doldurulur) ---- PencereOrtami ort_; // ---- Girdi jest/hover/bağlam politika sistemleri ---- girdi::FareJestSistemi jestSis_; girdi::SahneHoverProbu hoverProbu_; girdi::BaglamPolitika baglamPol_; girdi::PolitikaKarar sonPolitika_; ImVec2 sonPolitikaKonum_{}; u32 sonPolitikaHedef_ = 0; u32 sonPolitikaHedefAlt_ = 0; // hedefin alt ordinali (datum düzlem 4/5/6 veya yüz) — Eskiz Aç için bool baglamMenuAcildi_ = false; // Bağlam motoru sağ-tık halkası kaynağı (Dalga 3): motor önerilerinin DERİN kopyası; // sonPolitika_ öğelerinin simge işaretçileri BU tampona bakar (menü ömrü boyunca stabil). std::vector sonMotorOneri_; // ---- Merkezi klavye kısayolu kaydı (BASİT/GLOBAL kısayollar) ---- // Bağlamsal (sketch/CAM/ölçüm-moduna bağlı) kısayollar tusBasildi if-zincirinde // KALIR; yalnız mod-bağımsız globaller (Ctrl+Z/Ctrl+Y) burada merkezileşir. fiko::FikoKisayolKaydi kisayollar_; // ---- Dinamik araç çubuğu + ayarlar penceresi durumu ---- ui::AracCubuguSistemi g_araclar_; ui::SeritCubugu g_serit_; // dinamik durum-farkında alt şerit toolbar AyarlarPencereDurum ayarlarDurum_; // ---- Polimorfik panel yöneticileri (per-window). kur()'da kaydedilir, kareCiz()'de // cizTumu() ile sürülür. İki grup (çizim SIRASI korunur, BİREBİR davranış): // panelYon_ : erken paneller (Ürün Ağacı/Işık/Özellikler/Olay/Makro/Eklenti). // panelYonGec_ : geç, bitişik batch (CAM Programı → Op → ParçaOp → Takım → // TakımEkseni → Feature → GSD → Analiz → Kesit → Döküm → CAM Sim). // Batch-arası dağınık paneller (hizli3d/ayarlar/sketcher/sketchAraclari) İşlemler // ve pah3D satır-içi bloklarıyla çevrili → yerlerinde tekil ciz() ile polimorfize. FikoPencereYonetici panelYon_; FikoPencereYonetici panelYonGec_; // Ctor-ref panelleri üye (kur()'da bağlanır, dağınık konumda ciz() çağrılır): std::unique_ptr hizli3dPanel_; FikoSurecYoneticiPanel* surecYonPanel_ = nullptr; // panelYon_ sahiplenir (forward drain besler) std::unique_ptr ayarlarPanel_; std::unique_ptr sketcherPanel_; std::unique_ptr sketchAraclariPanel_; // ---- Kare döngüsü zaman durumu ---- std::chrono::steady_clock::time_point baslangic_; std::chrono::steady_clock::time_point oncekiKare_; f32 kareSuresiMs_ = 0.0f; int kare_ = 0; f32 dt_ = 0.0f, zaman_ = 0.0f; // kareCiz için (loop'ta hesaplanır) std::chrono::steady_clock::time_point simdi_; // bu karenin zaman damgası (basış süreleri) std::vector hizliDunyaNokta_; // özel-yön kütük tamponu // ---- Görsel oto-senaryo durumu (--senaryo-dokum) ---- std::filesystem::path senaryoDizin_; int snAdim_ = -1; f32 snBekle_ = 0, snTimeout_ = 0; u32 snKutu_ = 0, snSim_ = 0; int snScrub_ = 0; }; // ---- Pencere sürücüsü ---------------------------------------------------------- // "Yeni Belge" = yeni fikocad.exe SÜRECİ (süreç izolasyonu; aynı-süreç çok-pencere // MDI'den vazgeçildi). Panel dışarı-taşıma ImGui multi-viewport ile (ViewportsEnable). // Çökme teşhisi: en son geçilen kare-içi adım (SEH handler cokme.log'a yazar; tampon // yok → çökmede kaybolmaz). Kare gövdesinde anahtar noktalarda güncellenir. // g_sonAdim -> fiko namespace (extern, header). Tanim asagida. // Son kullanılan dosyalar (MRU) — UYGULAMA-GLOBAL (belge değil app düzeyi; tüm // pencereler paylaşır). Startup'ta ayar'dan yüklenir (kur()), aç/kaydet'te güncellenir // + ayar'a kalıcı yazılır. Universal palet "Dosya" kaynağını besler. FikoSonDosyalar g_sonDosyalar; bool g_sonDosyalarYuklendi = false; // Lisans + komut denetim — UYGULAMA-GLOBAL (belge değil app düzeyi; tüm pencereler tek // lisans + toplu kullanım takibi paylaşır). Her belge penceresi kur()'da g_lisans.bagla // (kendi cekirdek'ini gate+takibe bağlar). Lisans uygulama-geneli olduğundan belge-başına DEĞİL. // Tanım lisans_yukle.cpp'de (dağınık kontrol noktaları da oradan erişir); burada // yalnız kısa ad — mevcut g_lisans kullanımları değişmeden çalışır. LisansSistemi& g_lisans = lisansSistemi(); bool g_lisansYuklendi = false; // kümülatif kullanım ayar'dan bir kez yüklendi mi bool g_orkestraEslesmeYuklendi = false; // Orchestrator eşleştirme deposu bir kez yüklendi mi bool g_lisansDosyaYuklendi = false; // .fkl app-global, MDI'de pencere başına DEĞİL #ifdef FIKO_URETICI_UI bool g_lisansUreticiAcik = false; // Lisans Üretici penceresi (yalnız üretici derlemesi) bool g_lisansYonetimAcik = false; // Yönetim Konsolu (uzaktan lisans/komut — üretici only) bool g_cihazMerkeziAcik = false; // Cihaz Merkezi (kart ızgarası — üretici only) #endif // Üretici modu: lisans üretici arayüzü YALNIZ bizde açılsın. Koşul, özel imza // anahtarının varlığı — o dosya müşteride ASLA bulunmaz. Menü komutu bu koşul // sağlanmadıkça hiç KAYDEDİLMEZ → müşteride menüde/palette/aramada yok (gizli değil, // yok). Ortam değişkeni ikinci yol: anahtarı taşınabilir diskte tutanlar için. #ifdef FIKO_URETICI_UI bool lisansUreticiModu() { if (const char* e = std::getenv("FIKO_URETICI"); e && *e && *e != '0') return true; return lisansUreticiMakinesiMi(); } #endif // FIKO_URETICI_UI // Otomatik güncelleme servisi — UYGULAMA-GLOBAL (sürüm altyapısı scaffold; §C). // Dalga 1'de yalnız bagla + SemVer/manifest mantığı; network/indirme SONRAKİ dalga. // Tek örnek guncelleme_kontrol.cpp'de (fiko::guncellemeServisi tekil); burada // anon-namespace referans alias'ı — mevcut g_guncelleme kullanımları değişmez. GuncellemeServisi& g_guncelleme = guncellemeServisi(); GeriBildirimServisi& g_geriBildirim = geriBildirimServisi(); // Süreçler-arası merkezi komut köprüsü (app-global; her fikocad.exe bir örnek). Açılışta // baslat, kare-başına drain → komutCalistir("ipc"), çıkışta durdur. // NOT: Tanım isimsiz namespace DIŞINA, namespace fiko içine taşındı (aşağıda) — // aksi halde internal linkage alır ve extern fiko::g_surecKopru (kafa_penceresi.cpp) // ile LNK2001 (çözülmemiş dış sembol) hatası verir. // MRU'ya ekle + ayar'a kalıcı yaz (dosya aç/kaydet başarı yollarından çağrılır). static void sonDosyaKaydet(const std::string& yol) { g_sonDosyalar.ekle(yol); ayarYaz("son_dosyalar", g_sonDosyalar.serialize()); } } // namespace (ayar bağı isim alanı dışında yaşamalı) namespace fiko { bool& aygitKaybindaYenidenBaslat() { static bool b = true; return b; } // Süreçler-arası merkezi komut köprüsü (app-global; her fikocad.exe bir örnek). // Gerçek (external-linkage) tanım burada — namespace fiko içinde — olmalı ki // kafa_penceresi.cpp'deki "extern SurecKopru g_surecKopru;" bunu çözebilsin. SurecKopru g_surecKopru; } // namespace fiko namespace { int pencereDongusu(int otoTestKare, bool senaryoDokum) { // OOP Dalga 1 + MDI: pencereler vector'ü. [0] = ana pencere; "Yeni Belge" ek örnek. // Ana pencere VulkanBaglam+BoruHatlari'nı sahiplenir; ek pencereler paylaşır. // Açılış göstergesi: ağır init (Vulkan aygıt/pipeline/shader + ImGui font atlası // kurucuda; kur() = komut/tezgah + originKur; baslat() = kare-öncesi) ImGui // ilk-karesinden ÖNCE olduğundan hafif GDI splash gerçek adımlarla ilerletilir. // Oto-test/senaryo modunda gösterilmez (görünmez/otomatik akış). const bool splashGoster = otoTestKare == 0 && !senaryoDokum; // Oto-test/senaryo: her kare koşulsuz çizilmeli (deterministik akış + PNG dökümü); // on-demand uyku sonlandırmayı bozar → bu modlarda kapat. if (otoTestKare || senaryoDokum) kareIstekAc(false); AcilisSplash splash; if (splashGoster) { splash.goster(); splash.ilerle(0.08f, "Vulkan aygıtı ve arayüz hazırlanıyor..."); } std::vector> pencereler; pencereler.push_back(std::make_unique( otoTestKare, senaryoDokum, /*anaMi=*/true, /*paylasilanVulkan=*/nullptr, /*paylasilanHatlar=*/nullptr)); if (splashGoster) splash.ilerle(0.60f, "Komutlar ve çalışma tezgahı kuruluyor..."); pencereler[0]->kur(); if (splashGoster) splash.ilerle(0.85f, "Sahne hazırlanıyor..."); pencereler[0]->baslat(); if (splashGoster) { splash.ilerle(1.0f, "Hazır"); splash.kapat(); // yumuşak fade-out + yok et (yalnız açılışta) } // Süreçler-arası merkezi komut köprüsü: bu süreci kaydet + komut sunucusunu başlat. g_surecKopru.baslat("Adsız"); // belge adı dosya aç/kaydet ile güncellenir (heartbeat yazar) // Komut satırı dosyası (Windows "Birlikte aç"/dosya ilişkilendirmesi + otomasyon): // sürükle-bırak yolunun aynısı (lisans kapısı orada). Argüman yoksa hiçbir şey değişmez. pencereler[0]->dosyaYoluAc(komutSatirindanDosya()); // Ayar yüklemesi on-demand'ı açmış olabilir → oto-test/senaryo'da kesin kapat // (bu modlar her kareyi koşulsuz çizmeli; uyku deterministik akışı bozar). if (otoTestKare || senaryoDokum) kareIstekAc(false); int cikisKodu = 0; bool anaKapandi = false; auto sonrakiKare = std::chrono::steady_clock::now(); // FPS tavanı kadans zamanlayıcısı // ---- GÖZCÜ (watchdog): DONMA teşhisi ---- // ÖLÇÜLDÜ 07-21: 100 ms aralıklı (insan hızı) pencere sürüklemesinde uygulama // ÇÖKMÜYOR ama DONUYOR (Responding=False; senkron SetWindowPos geri dönmüyor) — // Fikri'nin "pencerenin içi donuyor" şikayeti. Çökmeden farklı olarak süreç YAŞADIĞI // için LOG YAZILABİLİR: ayrı bir iş parçacığı kalp atışını izler, eşiği aşarsa // takıldığı adımı (g_sonAdim / gunluk::g_teshisAdim) ve WSI izini döker. // Eşik AyarSistemi: `render.gozcu_saniye` (0 = kapalı). std::atomic kalpAtisi{0}; std::atomic gozcuDur{false}; std::thread gozcu([&kalpAtisi, &gozcuDur] { u64 sonGorulen = 0; int sessizSn = 0; bool yazildi = false; while (!gozcuDur.load(std::memory_order_relaxed)) { std::this_thread::sleep_for(std::chrono::seconds(1)); const int esik = gozcuSaniye(); if (esik <= 0) { sessizSn = 0; yazildi = false; continue; } const u64 simdi = kalpAtisi.load(std::memory_order_relaxed); if (simdi != sonGorulen) { sonGorulen = simdi; sessizSn = 0; yazildi = false; continue; } if (++sessizSn < esik || yazildi) continue; yazildi = true; // tek sefer yaz (donma sürerken log şişmesin) gunluk::hata("[GOZCU] {} saniyedir kare tamamlanmadi — UYGULAMA DONMUS OLABILIR", sessizSn); gunluk::hata("[GOZCU] son adim: {}", g_sonAdim ? g_sonAdim : "?"); gunluk::hata("[GOZCU] teshis adimi: {}", gunluk::g_teshisAdim ? gunluk::g_teshisAdim : "?"); } }); struct GozcuKapat { std::atomic& d; std::thread& t; ~GozcuKapat() { d.store(true); if (t.joinable()) t.join(); } } gozcuKapat{gozcuDur, gozcu}; // GÖZCÜ kalp atışı: kalpAtisi'ni DÖNGÜ TURU değil, ÇİZİLEN KARE artırır (aşağıda // kareCalistir içinde). ÖLÇÜLDÜ 07-21: fence zaman aşımında kare ATLANIYOR ama döngü // dönüyordu → tur-sayacı donmayı GÖREMİYORDU. g_gozcuKalp = &kalpAtisi; while (!anaKapandi) { // On-demand render: hiçbir pencere kare istemiyorsa girdiye kadar uyu. Girdi // (fare/klavye/boyut) anında uyandırır; gelmezse ~100 ms sonra bir yoklama karesi // (anim/sim/async durumu yeniden değerlendirilir → gerekirse tam hıza döner). // Boştaki GPU/pil yükünü 200+ FPS'ten düşürür; oto-test/senaryo'da devre dışı. if (kareIstekAcik() && kareIstek() == 0) { MsgWaitForMultipleObjectsEx(0, nullptr, 100, QS_ALLINPUT, 0); kareIste(1); // en az bir yoklama karesi } // TEK Win32 mesaj pompası → thread'in TÜM pencerelerine dağıtır. Pencere::mesajPompala(); // Her açık pencere için bir kare çiz. kareCalistir false → o pencere çıkmak ister. // GUARD: bir kare içindeki istisna (feature/render/pick) UYGULAMAYI ÇÖKERTMEZ — yakala, // logla, devam. (Aksi halde exception main loop'tan çıkar → stack-unwind teardown'da // std::thread → "resource deadlock". Bir kare atlanır, uygulama açık kalır.) for (auto& p : pencereler) { if (p->kapatildi()) continue; try { if (!p->kareCalistir()) { if (p->anaMi()) { anaKapandi = true; break; } // Ek pencere: kapanmayı işaretle (aşağıda vector'den çıkar). } } catch (const std::exception& e) { gunluk::hata("Kare istisnasi (yakalandi, devam): {}", e.what()); } catch (...) { gunluk::hata("Kare bilinmeyen istisnasi (yakalandi, devam)"); } } if (kareIstekAcik()) kareTuketildi(); // bir kare işlendi → istek bir azalır // Güncelleme "Kur & Yeniden Başlat" istendi → uygulamayı kapat (updater EN SON, // yıkım sonrası başlatılır; dosya kilitleri bırakılınca değiştirir + yeniden açar). if (g_guncelleme.kurulumIstendi()) anaKapandi = true; if (anaKapandi) break; // Kapatılan (WM_CLOSE) veya çıkmak isteyen ek pencereleri kaldır. Ana pencere // kapanınca döngü biter (uygulama çıkışı). Kapanan pencere yıkımı ~FikoBelgePenceresi. for (auto& p : pencereler) if (p->anaMi() && p->kapatildi()) anaKapandi = true; if (!anaKapandi) { std::erase_if(pencereler, [](const std::unique_ptr& p) { if (p->anaMi()) return false; return p->kapatildi(); }); } // NOT: aynı-süreç "Yeni Belge" penceresi KALDIRILDI — yeni belge artık yeni // fikocad.exe süreci (Dosya → Yeni Belge komutu CreateProcessW). // FPS TAVANI: aktif renderde GPU'yu coşturmayı önle (idle zaten WaitMessage'la // ~10fps). sleep_until sabit kadans; geride kalınca sıfırla (spiral-of-death önle). // Oto-test/senaryo (kareIstekAcik()==false) sınırsız kalır. const int fps = hedefFps(); if (kareIstekAcik() && fps > 0) { sonrakiKare += std::chrono::microseconds(1000000 / fps); const auto simdi = std::chrono::steady_clock::now(); if (sonrakiKare > simdi) std::this_thread::sleep_until(sonrakiKare); else sonrakiKare = simdi; // geride kaldık → kadansı sıfırla } else { sonrakiKare = std::chrono::steady_clock::now(); } } // GPU aygıtı kaybı ile mi çıkıyoruz? Bu ÇÖKME sayılır: oturum dizini SİLİNMEZ → // açılışta oto-kayıt kurtarması belgeyi geri teklif eder. Ayrıca uygulama kendini // yeniden başlatır (kullanıcı elle kapat-aç yapmak zorunda kalmasın). const bool aygitKaybiCikis = !pencereler.empty() && pencereler[0]->vulkanBaglam().aygitKayipMi(); if (aygitKaybiCikis) gunluk::hata("GPU aygiti kaybi ile cikiliyor — oturum kurtarma icin KORUNUYOR"); else // Temiz kapanış işareti: oturum dizini silinir → açılışta kurtarma sorulmaz. otoKayit().temizKapanis(); // YENİDEN BAŞLATMA BURADA (yıkımdan ÖNCE): aygıt kayıpken Vulkan yıkımı sürücüde // DEVICE_LOST döndürüp süreci sessizce düşürebiliyor (ölçüldü 07-21: günlük "Eklenti // kaldırıldı"da kesiliyordu, yıkım sonrasına konan yeniden başlatma hiç koşmuyordu). // Çocuk süreç, yıkımı biten ebeveynle kısa süre birlikte yaşar — sakıncası yok. // // ⚠️ UZAK KOMUT KANALI BURADA DURDURULMALI: kanal iş parçacığı detached ve // `Cekirdek&` referansı tutuyor. Cekirdek yıkımı sırasında hâlâ *g_cekirdek // okursa dangling pointer → 0xC0000409 fast-fail. Ölçüldü: log "Eklenti // kaldırıldı"da kesiliyor, hemen ardından 0xC0000409. uzakKomutKanalDurdur(); // Aynı tehlike: canlı lisans kanalının detached iş parçacığı statik durum // nesnelerine erişiyor. O da kapanışta durdurulmalı. uzakCanliDurdur(); if (aygitKaybiCikis && aygitKaybindaYenidenBaslat() && !g_guncelleme.kurulumIstendi()) { g_sonAdim = "aygit kaybi: yeniden baslat"; wchar_t exeYol[MAX_PATH]{}; GetModuleFileNameW(nullptr, exeYol, MAX_PATH); // AÇIK BELGEYİ GERİ GETİR: argümansız başlatma BOŞ belge açıyordu — kullanıcı // kaza sonrası dosyasını elle yeniden açmak zorunda kalıyordu. MRU'nun ilk kaydı // = bu oturumda en son açılan/kaydedilen dosya; komut satırı yolu `dosyaYoluAc` // ile açılır (Explorer sürükle-bırakla AYNI yol → lisans kapısı orada). // Dosya yoksa/silinmişse argümansız açılır (eski davranış). std::wstring komutSatiri = std::wstring(L"\"") + exeYol + L"\""; if (!g_sonDosyalar.bos()) { const std::string& yol = g_sonDosyalar.hepsi().front(); if (!yol.empty() && std::filesystem::exists(yol)) { const int n = MultiByteToWideChar(CP_UTF8, 0, yol.data(), (int)yol.size(), nullptr, 0); std::wstring genis((std::size_t)(n > 0 ? n : 0), L'\0'); if (n > 0) MultiByteToWideChar(CP_UTF8, 0, yol.data(), (int)yol.size(), genis.data(), n); if (!genis.empty()) komutSatiri += L" \"" + genis + L"\""; } } STARTUPINFOW si{}; si.cb = sizeof(si); PROCESS_INFORMATION pi{}; if (CreateProcessW(nullptr, komutSatiri.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); gunluk::bilgi("Aygit kaybi sonrasi yeniden baslatildi (pid {})", (unsigned)pi.dwProcessId); } else { gunluk::hata("Yeniden baslatma basarisiz (CreateProcessW hata {})", (unsigned)GetLastError()); } } g_sonAdim = "yikim: surecKopru.durdur"; g_surecKopru.durdur(); // IPC sunucusunu durdur + kayıttan çık // Ana pencere kapandı → aygıtı idle bekle (güvenli yıkım). g_sonAdim = "yikim: pencere bekle"; for (auto& p : pencereler) p->bekle(); // WTEC kafa singleton'ının Vulkan kaynaklarını (ViewportHedef/Belge) aygıt canlıyken // ve VulkanBaglam (ana pencere) yıkılmadan ÖNCE serbest bırak (yıkım sırası kritik). g_sonAdim = "yikim: kafa panelleri reset"; for (int i = 0; i < kKafaSayi; ++i) { // her iki kafa (Diagonal/Ozmo) GPU kaynakları kafaDurum((KafaTip)i).hedef.reset(); kafaDurum((KafaTip)i).belge.reset(); } // Kümülatif komut kullanımını kalıcıya yaz (oturumlar arası telemetri; app-global tek yazım). g_sonAdim = "yikim: ayarYaz"; ayarYaz("lisans_kullanim", g_lisans.kullanimSerialize()); ayarYaz("orkestra_eslesme", g_orkestraKule.eslesmeKaydet()); // Dönemsel kota sayaçları KALICI olmalı: yoksa kullanıcı uygulamayı kapatıp açarak // "ayda 100 G-kod" hakkını sonsuz kez tazeler. ayarYaz("lisans_kota", g_lisans.kotaSerialize()); // YIKIM SIRASI KRİTİK: ek pencereler VulkanBaglam+BoruHatlari'nı ana pencereden // PAYLAŞIR (referans). Ana pencere (pencereler[0]) yıkılırsa paylaşılan aygıt gider // → ek pencerelerin ~ImGuiKatman/~PencereYuzeyi dangling olur. Bu yüzden önce // ek pencereleri (sondan başa), en son ana pencereyi yık. g_sonAdim = "yikim: ek pencereler pop_back"; while (pencereler.size() > 1) pencereler.pop_back(); // ek pencereler önce g_sonAdim = "yikim: ana pencere clear (~FikoBelgePenceresi/~ImGuiKatman)"; pencereler.clear(); // ana pencere en son // Güncelleme kurulumu EN SON: manuel "Kur" ya da Otomatik mod + doğrulanmış paket. // Vulkan/pencere yıkımı bitti, süreç çıkmak üzere → fiko_updater PID'imizi bekler, // kilitler bırakılınca dosyaları değiştirir + uygulamayı yeniden başlatır. if (g_guncelleme.kurulumIstendi() || (g_guncelleme.durum() == GuncellemeServisi::Durum::Dogrulandi && g_guncelleme.mod() == GuncellemeServisi::Mod::Otomatik && !g_guncelleme.indirilenYol().empty())) { g_sonAdim = "yikim: guncelleme kur (fiko_updater)"; guncellemeKurBaslat(g_guncelleme); } g_sonAdim = "yikim: tamamlandi (return)"; return cikisKodu; } // ---- FikoBelgePenceresi::kur() — eski calistir setup gövdesi (BİREBİR) --------- // Makro dosyasını "Makro" grubunda palet komutu olarak kaydet (kur'daki mevcut dosyalar + // öneri çipi kabulünde yeni dosya — makro-öneri AÇIK maddesi: palete otomatik düşer). // Ad = dosya adı (uzantısız); aynı ad tekrar kaydolmaz (yeniden giriş güvenli). static void makroKomutEkle(Cekirdek& cekirdek, betik::MakroMotor& makro, const std::string& yol) { const std::string ad = std::filesystem::path(yol).stem().string(); if (cekirdek.menuler().bul("Makro", ad.c_str())) return; cekirdek.menuler().ekle( {"Makro", ad, "🔮", 0, [yol, &makro](Cekirdek&) { makro.dosyaCalistir(yol); }}); } // ---- Toolbar kişiselleştirme ayarları — tanım belge_penceresi.hpp'de, burada örnek ---- // NOT: fiko:: namespace'i içinde tanımlanmalı; aksi halde global linkage alır ve // ayar_kayitlari.cpp'deki "fiko::toolbarAyar()" çağrısı LNK2001 verir. static ToolbarAyar g_toolbarAyar; } // namespace (toolbarAyar tanimi isim alani disinda yasamali) namespace fiko { ToolbarAyar& toolbarAyar() { return g_toolbarAyar; } } // namespace fiko namespace { // Alt yatay ikon toolbar — ekranın altına ortalı, içeriğe göre otomatik boyutlu. // Düğmeler sığmazsa yatay scroll (sol-tık sürükle). Border + kişiselleştirme ayarlı. static void altYatayAracCubugu(const ImGuiViewport* vp, UygulamaBaglam& bglm) { if (!g_toolbarAyar.goster) return; struct Aract { const char* etiket; const char* ipucu; ImVec4 renk; }; static const Aract araclar[] = { {"🔰", "Yeni", ImVec4(0.95f, 0.55f, 0.10f, 1.0f)}, {"A", "Aç", ImVec4(0.20f, 0.55f, 0.85f, 1.0f)}, // {"K", "Kaydet", ImVec4(0.20f, 0.65f, 0.35f, 1.0f)}, // {"Sç", "Seç", ImVec4(0.85f, 0.75f, 0.15f, 1.0f)}, // {"Ta", "Taşı", ImVec4(0.85f, 0.55f, 0.15f, 1.0f)}, // {"Dö", "Döndür", ImVec4(0.55f, 0.35f, 0.75f, 1.0f)}, // {"Öl", "Ölçek", ImVec4(0.15f, 0.65f, 0.65f, 1.0f)}, // {"Öç", "Ölçüm", ImVec4(0.20f, 0.55f, 0.85f, 1.0f)}, // {"Kş", "Kesit", ImVec4(0.75f, 0.30f, 0.30f, 1.0f)}, // {"Ay", "Ayarlar", ImVec4(0.45f, 0.45f, 0.50f, 1.0f)}, }; static const int adetBtn = (int)(sizeof(araclar) / sizeof(araclar[0])); const ToolbarAyar& ay = g_toolbarAyar; // Pencere boyutu: içeriğe göre otomatik, ekran dışına taşmaz const float icerikG = adetBtn * ay.dugmeCap + (adetBtn - 1) * ay.aralik + ay.icPay * 2.0f; const float maxG = vp->WorkSize.x - ay.kenarPay * 2.0f; const float pencereG = std::min(icerikG, maxG); const float pencereY = ay.dugmeCap + ay.icPay * 2.0f; // Yatay orta, dikey alt const float px = vp->WorkPos.x + (vp->WorkSize.x - pencereG) * 0.5f; const float py = vp->WorkPos.y + vp->WorkSize.y - pencereY - ay.altBosluk; ImGui::SetNextWindowPos(ImVec2(px, py)); ImGui::SetNextWindowSize(ImVec2(pencereG, pencereY)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(ay.icPay, ay.icPay)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, ay.dugmeCap * 0.5f + ay.icPay); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ay.aralik, 0)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, ay.dugmeCap * 0.5f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, ay.borderKalinlik); ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(ay.bgR, ay.bgG, ay.bgB, ay.saydamlik)); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(ay.borderR, ay.borderG, ay.borderB, ay.borderA)); ImGui::Begin("##AltYatayToolbar", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar); // ---- Mouse-drag yatay scroll ---- static float s_surukleSonX = 0.0f; static bool s_surukleAktif = false; const bool pencereHover = ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); if (pencereHover && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { s_surukleAktif = true; s_surukleSonX = ImGui::GetMousePos().x; } if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) s_surukleAktif = false; if (s_surukleAktif) { const float simdiX = ImGui::GetMousePos().x; const float delta = s_surukleSonX - simdiX; s_surukleSonX = simdiX; if (std::abs(delta) > 0.5f) ImGui::SetScrollX(ImGui::GetScrollX() + delta); } // ---- Düğmeler: sığıyorsa ortala, taşıyorsa scroll ---- const float scrollAlan = ImGui::GetContentRegionAvail().x; const float toplamIcerik = adetBtn * ay.dugmeCap + (adetBtn - 1) * ay.aralik; if (toplamIcerik < scrollAlan) ImGui::SetCursorPosX(ay.icPay + (scrollAlan - toplamIcerik) * 0.5f); for (int i = 0; i < adetBtn; ++i) { const Aract& a = araclar[i]; if (i > 0) ImGui::SameLine(0.0f, ay.aralik); ImGui::PushID(a.ipucu); ImGui::PushStyleColor(ImGuiCol_Button, a.renk); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(std::min(a.renk.x*1.15f, 1.0f), std::min(a.renk.y*1.15f, 1.0f), std::min(a.renk.z*1.15f, 1.0f), 1.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, a.renk); const bool tiklandi = ImGui::Button(a.etiket, ImVec2(ay.dugmeCap, ay.dugmeCap)); ImGui::PopStyleColor(3); if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) ImGui::SetTooltip("%s", a.ipucu); if (tiklandi && std::strcmp(a.ipucu, "Aç") == 0) acKomut(bglm); // birleşik "Aç" diyaloğu (dosya_islemleri.hpp) → yolAc yönlendiricisi else if (tiklandi && std::strcmp(a.ipucu, "Yeni") == 0) fiko::yeniBelgeKomut(bglm); // "Yeni Belge" = yeni fikocad.exe süreci ImGui::PopID(); } ImGui::End(); ImGui::PopStyleColor(2); // WindowBg + Border ImGui::PopStyleVar(5); // Padding + Rounding + ItemSpacing + FrameRounding + BorderSize } void FikoBelgePenceresi::kur() { // Üye altyapıya eski isimlerle alias (gövde bare isimleri değişmesin). const int otoTestKare = otoTestKare_; const bool senaryoDokum = senaryoDokum_; Pencere& pencere = pencere_; VulkanBaglam& vulkan = vulkan_; ImGuiKatman& arayuz = arayuz_; const std::string& shaderDizin = shaderDizin_; const std::string& olayLogYol = olayLogYol_; BoruHatlari& hatlar = hatlar_; CizimOnbellek& anaCizim = anaCizim_; BelgeYonetici& belgeYon = belgeYon_; girdi::FareJestSistemi& jestSis = jestSis_; girdi::SahneHoverProbu& hoverProbu = hoverProbu_; girdi::BaglamPolitika& baglamPol = baglamPol_; ui::AracCubuguSistemi& g_araclar = g_araclar_; ui::SeritCubugu& g_serit = g_serit_; (void)otoTestKare; (void)senaryoDokum; (void)hatlar; (void)anaCizim; PencereOrtami& ort = ort_; ort.imguiPtr = &arayuz; // ana pencerenin ImGui katmanı // Kalıcı tema: son seçilen tema yeniden başlatmada geri yüklenir // (ImGuiKatman öntanımlı "Koyu" uygular; ayardan farklıysa üstüne seç). ui::temaSec(ayarOku("tema", "Koyu").c_str()); // shaderDizin/olayLogYol/hatlar/anaCizim/belgeYon artık SINIF ÜYESİ (yukarıda // member-alias'landı) → burada yeniden tanımlanmaz. Viewport pipeline + ana // belgenin çizim önbelleği + BelgeYonetici pencere ömrü boyunca yaşar. // ---- BELGE (MDI Dalga 1b, docs/mdi_blueprint.md): dökümanın tüm durumu tek // nesnede (Cekirdek + Sahne + sistemler + kamera). Aşağıdaki referans takma- // adları gövdeyi DEĞİŞTİRMEDEN eski isimlerle çalıştırır; Dalga 2'de // BelgeYonetici N örnek + belge pencereleri bu nesneyi çoğaltır. ----------- ort.belgePtr = std::make_unique(vulkan, shaderDizin); Belge& belge = *ort.belgePtr; SecimGpu& secim = belge.secim; Cekirdek& cekirdek = belge.cekirdek; Sahne& sahne = belge.sahne; SecimSistemi& secimSis = belge.secimSis; OlcumSistemi& olcum = belge.olcum; AlanSecim& alan = belge.alan; betik::MakroMotor& makro = belge.makro; eklenti::EklentiYonetici& eklentiler = belge.eklentiler; cam::CamSistemi& camSis = belge.camSis; parca::PartDesignSistemi& partD = belge.partD; gsd::GsdSistemi& gsdSis = belge.gsdSis; analiz::AnalizSistemi& analizSis = belge.analizSis; PmiSistemi& pmiSis = belge.pmiSis; DokumSistemi& dokumSis = belge.dokumSis; SahneRefSecim& refSecim = belge.refSecim; OrbitKamera& kamera = belge.kamera; const std::string& makroDizin = makroDizin_; // sınıf üyesi FareDurum& fare = ort.fare; // "Hızlı 3D" ağırlık/kütük widget'ı (long-press ile açılır, nesneye özel) // Durum TEK struct'ta toplanır (HizliUIDurum → hizli3d_penceresi.hpp); aşağıdaki // bare local'lar üyelerine REFERANS bağlanır → dev döngü gövdesi AYNEN kalır, // pencere çizimi modüle taşınır (sketchDurum tasarım deseninin aynısı). HizliUIDurum& hizliDurum = ort.hizliDurum; bool& hizli3dAcik = hizliDurum.hizli3dAcik; u32& hizli3dKimlik = hizliDurum.hizli3dKimlik; // hizli3dUstte / hizliOzkutle yalnız pencere modülünce kullanılır (hizliDurum // üyesi) → main.cpp'de bare local referans yok (ölü referans üretmemek için). int& hizliKutukMod = hizliDurum.hizliKutukMod; // 0=eksen-hizalı, 1=min-OBB, 2=özel yön bool& hizliKutukSahne = hizliDurum.hizliKutukSahne; // hesaplanan kütüğü 3D sahnede tel-kafes göster f32& hizliKutukSaydam = hizliDurum.hizliKutukSaydam; // sahne kütük dolgu saydamlığı (0=tel, 0.6=dolu) int& hizliYonSecim = hizliDurum.hizliYonSecim; // yön seçim modu: 0=kapalı, 1=1.yön bekliyor, 2=2.yön bekliyor // Pick filtresi yedeği: refSecim/camSecim/olcum modları filtreyi zorla ezer; // mod bitince kullanıcının 1/2/3/4 seçimi geri yüklenir (aksi halde sızar). bool& pickFiltreYedekVar = ort.pickFiltreYedekVar; bool& pickYedYuz = ort.pickYedYuz; bool& pickYedKenar = ort.pickYedKenar; bool& pickYedNokta = ort.pickYedNokta; bool& pickYedGovde = ort.pickYedGovde; Vec3f& hizliYon1 = hizliDurum.hizliYon1; // seçilen yönler (dünya-uzay, yüz normali) Vec3f& hizliYon2 = hizliDurum.hizliYon2; bool& hizliYon1Var = hizliDurum.hizliYon1Var; bool& hizliYon2Var = hizliDurum.hizliYon2Var; Vec3f (&hizliKose)[8] = hizliDurum.hizliKose; bool& hizliKutuVar = hizliDurum.hizliKutuVar; Vec3f& hizliBoyut = hizliDurum.hizliBoyut; // kare-başı hesap PusulaDurum& pusula = ort.pusula; KesitGizmoDurum& kesitGizmo = ort.kesitGizmo; // 3D sürüklemeli kesit düzlemi tutamacı SecimYigini& secimYigini = ort.secimYigini; // Selection Stack (Alt+tık aday yığını) bool& hizliGorunumAcik = ort.hizliGorunumAcik; // Dockable panel görünürlüğü (X ile kapanır, Pencereler menüsünden açılır). // Özellikler ayrı yüzen pencere (ana pencerenin üstünde), Olay Denetçi ve // Özellikler varsayılan kapalı — sürekli göz önünde olmasın. // ---- Işık durumu (IsikNesnesi → shader ModelItme). Varsayılanlar eski sabit // shader değerlerini birebir üretir; ışık pencereleri/ağaç bunları ayarlar. ---- IsikDurum& isikDurum = ort.isikDurum; bool& panelIsik = ort.panelIsik; bool& isikUstte = ort.isikUstte; bool& panelAgac = ort.panelAgac; bool& panelOzellik = ort.panelOzellik; bool& panelOlay = ort.panelOlay; bool& agacIcinde = ort.agacIcinde; // CATIA spec tree: ağaç 3B görünümün içinde (overlay) f32& agacSahneOlcek = ort.agacSahneOlcek; // sahne-içi (P2) ağaç zoom f32& agacPanDelta = ort.agacPanDelta; bool& agacSigdir = ort.agacSigdir; bool& ozellikAcikOnceki = ort.ozellikAcikOnceki; // Özellikler penceresi açılış kenarı (öne getirme) // K2: Seçim değişince ürün ağacında seçili satıra bir kez kaydır (her kare değil). bool& agacKaydirGerek = ort.agacKaydirGerek; // Ayrık paneller için "üstte tut" (pin) durumları — varsayılan AÇIK: ayrık pencereler // ana pencerenin arkasına düşmesin (başlık çubuğu düğmesiyle kapatılabilir). bool& agacUstte = ort.agacUstte; bool& ozellikUstte = ort.ozellikUstte; bool& olayUstte = ort.olayUstte; bool& islerUstte = ort.islerUstte; bool& olcumUstte = ort.olcumUstte; bool& camUstte = ort.camUstte; // CAM üretim ağacı artık ANA Ürün Ağacı'nda yaşar (camAgacTopla sağlayıcı). bool& panelIsler = ort.panelIsler; // İşlemler (görev ilerleme) penceresi görünürlüğü bool& panelMakro = ort.panelMakro; bool& panelEklenti = ort.panelEklenti; // Makro Konsolu / Eklenti Yöneticisi // ---- Sketcher (2B eskiz) UI durumu → sketch_ui.hpp'ye taşındı (SketchUIDurum) ---- // Durum TEK struct'ta toplanır; aşağıdaki bare local'lar SketchUIDurum üyelerine // REFERANS'tır → dev döngü gövdesi + tüm yardımcı lambda'lar AYNEN kalır (davranış // birebir), ama modüller (UygulamaBaglam::sketch) aynı durumu paylaşır. SketchUIDurum& sketchDurum = ort.sketchDurum; PaftaUIDurum& paftaDurum = ort.paftaDurum; // teknik resim modu (pafta_ui.hpp) Varlik& aktifPafta = paftaDurum.aktifPafta; Varlik& aktifSketch = sketchDurum.aktifSketch; // düzenlenen sketch (Sketcher workbench) bool& panelSketch = sketchDurum.panelSketch; // Sketcher paneli görünürlüğü std::string& sketchArac = sketchDurum.sketchArac; // "" | "cizgi" | "dikdortgen" | "daire" | "nokta" std::vector>& sketchNoktalar = sketchDurum.sketchNoktalar; // bekleyen tık uv'leri std::vector& sketchSecili = sketchDurum.sketchSecili; // kısıt için seçilen eskiz geometri kimlikleri std::vector& sketchSeciliKisit = sketchDurum.sketchSeciliKisit; // sahnede seçilen kısıt sembolleri // ---- CATIA tarzı Sketch Tools çekirdeği (SmartPick snap + canlı giriş) ---- // NOT: grid/snap/yapı/giriş vb. UI durum alanları artık sketch_ui.cpp (SketchUIDurum) // üzerinden erişilir; yalnız main.cpp'de kalan (tık-dispatch/oto-kısıt) alanlara alias. eskiz::SnapSonuc& sonSnap = sketchDurum.sonSnap; // son çözülen snap (tık-dispatch) bool& sketchYapiMod = sketchDurum.sketchYapiMod; // çizilen eleman construction (sketchYeniEleman) bool& sketchOtoGeoKisit = sketchDurum.sketchOtoGeoKisit; // oto geometrik kısıt (sketchOtoCakisik) int& sketchPolyKenar = sketchDurum.sketchPolyKenar; // çokgen kenar sayısı (sketchAracTikSayi/Tamamla) // ---- 2B Düzenleme (Operations) durumu ---- // sketchArac düzenleme moduna geçince (trim/fillet/ayna/otele/...) bu değişkenler // çoklu-tık akışını yürütür. sketchDuzenFaz: 0=eleman seç, 1=eksen/2.eleman/ref seç. int& sketchDuzenFaz = sketchDurum.sketchDuzenFaz; // düzenleme akış fazı (araç bazlı) std::array& sketchDuzenRef = sketchDurum.sketchDuzenRef; // referans nokta (otele/döndür/ölçek/ofset) char (&sketchDuzGiris)[4][32] = sketchDurum.sketchDuzGiris; // düzenleme değer metinleri (sketchDuzenSec temizler) // ---- 3B Pah / Kenar Yuvarlatma (CATIA Chamfer/Fillet) ---- bool& pah3DAcik = ort.pah3DAcik; // 3B pah/yuvarlat değer penceresi açık mı bool& pah3DYuvarlat = ort.pah3DYuvarlat; // true=fillet(yarıçap), false=chamfer(mesafe) char (&pah3DGiris)[32] = ort.pah3DGiris; // değer metni size_t& oncekiIsSayi = ort.oncekiIsSayi; // yeni iş gelince pencereyi otomatik geri getir // SURUKLE_CANLI namespace-scope sabit (yukarıda) → burada yeniden tanımlanmaz. u32& olcumSurukle = ort.olcumSurukle; // etiketi sürüklenen ölçüm kimliği (0=yok, SURUKLE_CANLI=canlı) int& olcumSurukleMod = ort.olcumSurukleMod; // 0=ofset kaydır, 1=döndür (sağ sürükleme) // Özellik düzenleme undo: widget aktifleşince pusula alınır, bırakışta DuzenleIslem DugumPusula& ozellikEski = ort.ozellikEski; std::string& durumMesaj = ort.durumMesaj; f32& durumMesajBitis = ort.durumMesajBitis; f32& simdikiZaman = ort.simdikiZaman; // Kamera icin son karenin izdusum durumu (girdi geri cagrimlarindan erisim) f32& sonEnBoy = ort.sonEnBoy; f32& sonG = ort.sonG; f32& sonY_ = ort.sonY_; // Viewport (MDI 3D pencere) durumu uint64_t& vpDoku = ort.vpDoku; VkImageView& vpKayitliGorunum = ort.vpKayitliGorunum; u32& istenenVpG = ort.istenenVpG; u32& istenenVpY = ort.istenenVpY; f32& vpRectMinX = ort.vpRectMinX; f32& vpRectMinY = ort.vpRectMinY; bool& vpHover = ort.vpHover; bool& dockKuruldu = ort.dockKuruldu; // ---- Girdi jest / hover / bağlam politika sistemleri (A) ---- // Anayasa: girdi modülü saf durum makinesi (sahne/ui bilmez). main her kare ham // girdiyi besler; sistemler tipli olaylar yayar. GİRDİ TÜKETİLMEZ → mevcut CATIA // kamera/seçim davranışı korunur (jest/hover yalnız gözlemcidir). // jestSis/hoverProbu/baglamPol SINIF ÜYESİ (yukarıda alias'landı); dinleyiciler // burada bağlanır (this yakalanır → nesne ömrü boyunca geçerli). // Hover değişince cekirdek olayı yay (VurguDegisti'ye ek: HedefTur soyutlaması). hoverProbu.dinle([&](const girdi::HoverBilgi& yeni, const girdi::HoverBilgi&) { kareIste(4); // on-demand: hover değişimi anında çizilsin (highlight gecikmesin) cekirdek.olaylar().yayinla( HoverDegisti{yeni.govdeId, static_cast(yeni.tur)}); }); // Jest çözülünce olay yay + bağlam politikası (menü kararı) — çözüm main döngüde. girdi::PolitikaKarar& sonPolitika = sonPolitika_; // jest listener doldurur; döngü çizer (guarded) ImVec2& sonPolitikaKonum = sonPolitikaKonum_; u32& sonPolitikaHedef = sonPolitikaHedef_; // bağlam menüsünün hedef gövdesi (inline seç/gizle/sil) u32& sonPolitikaHedefAlt = sonPolitikaHedefAlt_; // hedef alt ordinali (datum düzlem/yüz → Eskiz Aç) bool& baglamMenuAcildi = baglamMenuAcildi_; // bu jest bağlam menüsü açtı → fallback sağ-tık yolu atlanır jestSis.dinle([&](const girdi::FareJesti& j) { cekirdek.olaylar().yayinla(FareJestiOldu{ static_cast(j.tur), static_cast(j.hedef.tur), j.hedef.govdeId}); // Bağlam menüsü kararı: yalnız GUI üstünde değilken + GERÇEK sağ-tık/uzun-bas. // CATIA KORUMASI: Tik = kısa bas-birak (kamera döndürme SurukleBasla/Bitti üretir, // Tik ÜRETMEZ) → sağ+sürükle kamera menüyü AÇMAZ. karar saklanır; ana döngü guarded // çizer, jest sistemi girdiyi TÜKETMEZ (mevcut kamera/seçim davranışı korunur). // Orta tuş basılıyken (zoom/pan kamera) sağ-tık bağlam menüsü AÇILMASIN. // TUTARLILIK: normal sağ-Tik jest yolundan ÇIKARILDI → yalnız doğrudan yol (sahne.vurgu, // GARANTİ, tutarlı) açar. Jest zamanlaması "ara sıra farklı menü" yapıyordu. UzunBas kalır. if (!j.guiUstunde && !ImGui::IsMouseDown(ImGuiMouseButton_Middle) && j.tur == girdi::JestTur::UzunBas) { // Dalga 3: sağ-tık halkası bağlam MOTORUNDAN beslenir (registry meta + tip/nesne). // Motorun zengin kapsadığı bağlamlar (gövde/boş/yüz/kenar/vertex) → motor; diğerleri // (datum/GSD/CAM/ölçüm/sketch) mevcut BaglamPolitika'ya düşer → REGRESYON YOK. // TUTARLILIK: fallback hedefi sahne.vurgu'dan kurulur (doğrudan sağ-tık yolu ile AYNI // kaynak) → "hover ile seç+sağtık farklı menü" tutarsızlığı biter. jest.hedef hover // zamanlaması yerine sahne.vurgu güvenilir çözümü kullanılır. girdi::FareJesti js = j; js.hedef.govdeId = sahne.vurgu.govde; // DATUM (referans düzlem / Origin eksen-sistemi / eksen) → Govde menüsü (Eskiz Aç). // Kip Kenar/Yüz olsa BİLE datum'a kenar/pah menüsü açılmaz (kip = filtre, tür değil). Dugum* vd = sahne.bul(sahne.vurgu.govde); const bool datumDugum = vd && (vd->tip == DugumTip::Duzlem || vd->tip == DugumTip::Eksen || vd->tip == DugumTip::EksenSistemi || vd->tip == DugumTip::Nokta || vd->tip == DugumTip::Cizgi); if (!sahne.vurgu.govde) { // BOŞ yere sağ-tık → evrensel arama paleti (komut + sahne nesnesi + KYA ada-göre-seç). // Radyal "Hic" menüsü yerine arama (kullanıcı isteği); ana döngü açar (bglm henüz kapsamda değil). ort.bosSagTikArama = true; return; } else if (datumDugum) { js.hedef.tur = girdi::HedefTur::Govde; } else { switch (sahne.kip()) { case SecimKip::Yuz: js.hedef.tur = girdi::HedefTur::Yuz; break; case SecimKip::Kenar: js.hedef.tur = girdi::HedefTur::Kenar; break; case SecimKip::Nokta: js.hedef.tur = girdi::HedefTur::Nokta; break; default: js.hedef.tur = girdi::HedefTur::Govde; break; } } girdi::PolitikaKarar k; const auto& mk = belge.baglamMotoru.sonKarar(); if (!datumDugum && baglamMotorKapsar(mk.hedef.kategori) && !mk.ogeler.empty()) { sonMotorOneri_ = mk.ogeler; // derin kopya (simge işaretçileri buna bakar) baglamPolitikaYap(sonMotorOneri_, k); } else { baglamPol.tekWorkbench = dogrudanAyar().tekWorkbench; // ayar canlı (ucuz bool) // Montaj Kısıtları ön-koşulu: Yüz kipinde TAM İKİ, FARKLI gövdeden yüz seçili mi? // (BaglamPolitika "sahne bilmez" → tek bit dışarıdan iletilir, bkz. .hpp yorumu). { const auto& sl = sahne.secililer(); baglamPol.ikiYuzFarkliGovdeSecili = sahne.kip() == SecimKip::Yuz && sl.size() == 2 && sl[0].govde != sl[1].govde && sl[0].alt != 0 && sl[1].alt != 0; } k = baglamPol.karar(js, cekirdek.workbenchler().aktif()); // datum → politika (Eskiz Aç) } if (k.gecerli()) { // Menü zaten açıkken başka yere sağ-tık → yeni konumda YENİDEN aç. // Eski halka durumunu (açık alt-halka + animasyon) sıfırla, yoksa yeni menü // eski alt-halkayla gelir. ui::aracHalkasiSifirla("baglam"); sonPolitika = std::move(k); sonPolitikaKonum = ImVec2{j.ekranNokta.x, j.ekranNokta.y}; sonPolitikaHedef = sahne.vurgu.govde; // inline eylemler için hedef gövde (tutarlı) sonPolitikaHedefAlt = sahne.vurgu.alt; // datum düzlem 4/5/6 veya yüz ordinali (Eskiz Aç) baglamMenuAcildi = true; // fallback sağ-tık yolu bu tık için tetiklenmesin // Tüm bağlamlar aracHalkasi (radyal, nested) ile çizilir; ayrı açılış gerekmez. } } }); // ---- Dinamik araç çubuğu sistemi (B) ---- // g_araclar SINIF ÜYESİ (yukarıda alias'landı); içeriği komut kaydından SONRA // (ana döngü öncesi) doldurulur (bkz. "g_araclar kurulum"). bool& hoverBilgiAcik = ort.hoverBilgiAcik; // sahne hover bilgi kartı görünürlüğü (Görünüm'den toggle) // Referans geometri (Origin: XY/YZ/ZX düzlemleri + X/Y/Z eksenleri) — her belgede // kök referans; sketch/feature bunlara bağlanır. Undo'ya girmez. sahne.originKur(); // Açılış sahnesi BOŞ (yalnız Origin datum'ları + grid). Demo primitifler // (kutu/silindir/küre) kaldırıldı — kullanıcı tercihi. Ekleme komutları // (Geometri menüsü Kutu/Silindir/Küre) aynen durur. // Yerel helper lambda'ları bu blokta yaşar → bglm inşasında KOPYALANIR (kalıcı ort.bglm'e), // sonra blok kapanır. Bloktan sonra bglm.helper'a re-alias → komut'lar KALICI'yı yakalar // (belgeKur transient olsa da dangling YOK). boolIslem bilgiVer'e bağımlı → blok sonrası yeniden atanır. { const auto bilgiVer = [&](std::string m) { durumMesaj = std::move(m); durumMesajBitis = simdikiZaman + 4.0f; }; const auto tumunuSigdir = [&] { Aabbf tum; for (auto& d : sahne.dugumler()) { if (!d.yerelSinir.gecerliMi()) continue; // bos mesh (datum vb.) → NaN'a girme // Ekran-sabit referans (düzlem/eksen) fit'e girmez: görsel boyutu zoom'a bağlı // → dahil edilirse zoom-out sarmalı; ayrıca küçük parça sahnesinde baskın olur. if (d.tip == DugumTip::Duzlem || d.tip == DugumTip::Eksen || d.tip == DugumTip::EksenSistemi || d.tip == DugumTip::Nokta) continue; const Vec3f m = d.dunyaMerkez(); const f32 r = d.dunyaYaricap(); tum.genislet(m - Vec3f{r, r, r}); tum.genislet(m + Vec3f{r, r, r}); } if (tum.gecerliMi()) kamera.sigdir(tum.merkez(), tum.kosegen() * 0.5f + 0.5f); }; const auto secimiSigdir = [&] { const auto govdeler = sahne.seciliGovdeler(); if (govdeler.empty()) return; Aabbf tum; for (u32 g : govdeler) { if (Dugum* d = sahne.bul(g); d && d->yerelSinir.gecerliMi()) { const Vec3f m = d->dunyaMerkez(); const f32 r = d->dunyaYaricap(); tum.genislet(m - Vec3f{r, r, r}); tum.genislet(m + Vec3f{r, r, r}); } } if (tum.gecerliMi()) kamera.sigdir(tum.merkez(), tum.kosegen() * 0.5f + 0.3f); }; const auto seciliSil = [&] { const auto govdeler = sahne.seciliGovdeler(); if (!govdeler.empty()) cekirdek.uygula(std::make_unique(sahne, govdeler)); }; // K1: görünürlük tek yoldan — GorunumSaglayici.gorunurAyarla + GorunurlukDegisti // yayını (abone/makro/disk günlüğü tutarlı). Doğrudan d.gorunur yazımı YASAK. const auto gorunurDegistir = [&](u32 kimlik, bool deger) { GorunumSaglayici* gv = cekirdek.gorunum(kimlik); if (!gv) return; gv->gorunurAyarla(deger); const char* ad = ""; if (DokumanNesnesi* n = cekirdek.nesne(kimlik)) ad = n->ad().c_str(); cekirdek.olaylar().yayinla(GorunurlukDegisti{kimlik, deger}, ad); }; const auto boolIslem = [&](DugumTip islem) { if (sahne.seciliGovdeler().size() != 2) { bilgiVer("Boolean için tam 2 gövde seçin (Ctrl+tık)"); return; } cekirdek.uygula(std::make_unique(sahne, islem)); }; // ---- Uygulama bağlamı: modül dosyalarının paylaştığı yerel durum görünümü ---- // Üyeler main.cpp local'larına REFERANS bağlanır → tek doğruluk kaynağı burada; // dev döngü gövdesi aynen bare local'ları kullanır (davranış değişikliği yok). ort.bglmPtr = std::make_unique(UygulamaBaglam{ cekirdek, sahne, kamera, secimSis, olcum, alan, refSecim, makro, eklentiler, camSis, belge.mekanizma, belge.caeKurulumlar, partD, gsdSis, analizSis, belge.zekaSis, pmiSis, belge.malzemeKut, belge.malzemeAta, pencere, olayLogYol, makroDizin, panelOzellik, panelOlay, panelMakro, panelEklenti, ozellikAcikOnceki, ozellikUstte, olayUstte, ozellikEski, panelAgac, agacUstte, agacKaydirGerek, agacIcinde, agacSahneOlcek, agacPanDelta, agacSigdir, isikDurum, panelIsik, isikUstte, sketchDurum, hizliDurum, vpRectMinX, vpRectMinY, istenenVpG, istenenVpY, hoverBilgiAcik, bilgiVer, sonDosyaKaydet, [](const std::string& ad) { g_surecKopru.belgeAdiAyarla(ad); }, // belgeAdiAyarla tumunuSigdir, secimiSigdir, gorunurDegistir, seciliSil, boolIslem}); } // yerel helper lambda'ları burada ölür (kopyaları ort.bglm'de yaşar) UygulamaBaglam& bglm = *ort.bglmPtr; // Makro/MCP → belge dosya işlemleri köprüsü (motor katmanı UygulamaBaglam TANIMAZ). // fiko.belge.kaydet(yol) diyalogsuz kaydeder; fiko.belge.ac(yol) sürükle-bırak yolunu // kullanır (uzantıya göre içe aktarıcı + LİSANS kapısı orada). // Canlı performans köprüsü: fiko.perf.metrik() → Kontrol Merkezi ile AYNI kaynak. makro.perfCbAyarla([&ort](betik::MakroMotor::PerfOlcum& o) { const SistemMetrik& m = ort.metrikToplayici.metrik(); o.fps = m.fps; o.kareMs = m.kareSuresiMs; o.cpuYuzde = m.cpuYuzde; o.ramMB = m.ramMB; o.gpuYuzde = m.gpuYuzde; o.vramMB = m.vramKullanilanMB; return true; }); makro.belgeIoCbAyarla([&bglm](const std::string& eylem, const std::string& yol) { if (eylem == "kaydet") fiko::fikoKaydetYola(bglm, yol); else if (eylem == "ac") fiko::yolAc(bglm, yol); }); // Helper'ları KALICI bglm kopyalarına re-alias'la: komut lambda'ları bunları [&] yakalar → // setup transient bir kapsamda (belgeKur) çalışsa bile ort.bglm'de yaşadıkları için dangling // OLMAZ (ort pencere ömrü boyunca yaşar). Ana pencerede davranış birebir (bglm.X == yerel lambda kopyası). // Ekran görüntüsü delegesi: VulkanBaglam bglm'de yok (yaşam döngüsü burada) → // aktif viewport'un son karesini PNG olarak belleğe verir (geri bildirim penceresi). bglm.ekranYakala = [&vulkan](std::vector& png) { return vulkan.viewportPngBellek(png); }; auto& bilgiVer = bglm.bilgiVer; auto& tumunuSigdir = bglm.tumunuSigdir; auto& secimiSigdir = bglm.secimiSigdir; auto& seciliSil = bglm.seciliSil; auto& gorunurDegistir = bglm.gorunurDegistir; auto& boolIslem = bglm.boolIslem; // boolIslem yerel bilgiVer'i yakalamıştı (blokta öldü) → KALICI bglm.bilgiVer ile yeniden tanımla. bglm.boolIslem = [&bglm, &sahne, &cekirdek](DugumTip islem) { if (sahne.seciliGovdeler().size() != 2) { bglm.bilgiVer("Boolean için tam 2 gövde seçin (Ctrl+tık)"); return; } cekirdek.uygula(std::make_unique(sahne, islem)); }; // sketchGir sketchDikBak'a bağlı (aşağıda tanımlanır) → orada atanır. // Sketch mantık delegeleri sketchDikBak/sketchNesne (aşağıda tanımlanır) → orada bağlanır. // Dosya işlemleri (STEP/APT içe-dışa + .fkcache) dosya_islemleri.*'ye taşındı; komutlar kalıcı // bglm'i yakalar (thin lambda değil) → belgeKur transient olsa da dangling olmaz. // Tüm komutlar TEK registry'de (cekirdek.menuler): menü çubuğu, toolbar ve // komut paleti hep buradan beslenir — mükerrer tanım yok. Bir modül de aynı // MenuKaydi'ne MenuOge ekleyerek komut/araç katkılar (pluginable dikiş). // Ürün ağacı ECS hiyerarşisinden iç içe sürülür (grup + serbest geometri). cekirdek.agac().ekle(urunAgaciTopla); // CAM hiyerarşisi ana Ürün Ağacı'na sağlayıcı ile eklenir (AYRI ağaç yok). cekirdek.agac().ekle( [&camSis](Cekirdek& c, std::vector& kok) { camAgacTopla(c, camSis, kok); }); // Kalıcı ölçümler (kumpas/cetvel) ana Ürün Ağacı'nda "Ölçümler" grubu olarak yaşar. cekirdek.agac().ekle( [&olcum](Cekirdek& c, std::vector& kok) { olcumAgacTopla(c, olcum, kok); }); // Kinematik mekanizma (uzuv/mafsal) ana Ürün Ağacı'nda "Mekanizma" grubu olarak yaşar. cekirdek.agac().ekle([&belge](Cekirdek& c, std::vector& kok) { kinematikAgacTopla(c, belge.mekanizma, kok); }); auto& menuK = cekirdek.menuler(); // Kalıcı lisans kimlikleri: bundan SONRA kaydedilen her komut (bu dosya, ayarlar, // dinamik komut, eklenti DLL) kimliğini merkezî tablodan alır. menuK.kimlikCozucuAyarla(&lisansKimlik); // Satın alınmayan komutlar UI'da hiç görünmesin (menü/şerit/toolbar/palet/radyal). menuK.gorunurlukKapisiAyarla( [](const std::string& kimlik, const std::string& grup) { return lisansSistemi().ogeGorunur(kimlik, grup); }, [] { return lisansSistemi().politikaSurumu(); }); // wb = "" → global (her workbench'te görünür); aksi halde yalnız o workbench aktifken. const auto komut = [&](const char* grup, const char* ad, const char* simge, std::function fn, const char* wb = "", std::function etkinMi = nullptr, const char* altGrup = "") { MenuOge o{grup, ad, simge, 0, std::move(fn), std::move(etkinMi), wb}; o.altGrup = altGrup; // dolu → alt şeritte flyout kümesi (kategorili/kompakt) menuK.ekle(std::move(o)); }; // ---- Durum-duyarlı etkinMi yardımcıları (KARARLI yakalama) ---- // Menü/toolbar'da kur() sonrası çağrılır → YALNIZ heap-bağlı ref yakala: // sahne/aktifSketch member-ref'tir (kisitEtkin deseni). Yerel lambda YAKALAMA. const auto brepliGovdeVar = [&sahne]() -> bool { for (const Dugum& d : sahne.dugumler()) if (d.brep && d.brep->gecerliMi()) return true; return false; }; const auto etkinBrepli = [brepliGovdeVar](Cekirdek&) { return brepliGovdeVar(); }; // Feature ön-koşulu: aktif (düzenlenen) VEYA seçili sketch (CATIA: sketch seç → Pad aktif). const auto etkinSketch = [&aktifSketch, &sahne](Cekirdek&) { return aktifSketch != VARLIK_YOK || sahne.seciliSketch() != VARLIK_YOK; }; const auto etkinIkiGovde = [&sahne](Cekirdek&) { return sahne.seciliGovdeler().size() == 2; }; const auto etkinSecimVar = [&sahne](Cekirdek&) { return !sahne.seciliGovdeler().empty(); }; // Montaj Kısıtları ön-koşulu: Yüz kipinde, TAM İKİ yüz, FARKLI iki gövdeden seçili. // (baglamPolitikaYap'ta radyal menü koşulu da BİREBİR bunu kullanır — tek kaynak.) const auto ikiYuzFarkliGovdeMi = [&sahne]() -> bool { if (sahne.kip() != SecimKip::Yuz) return false; const auto& s = sahne.secililer(); if (s.size() != 2) return false; return s[0].govde != s[1].govde && s[0].alt != 0 && s[1].alt != 0; }; const auto etkinIkiYuzFarkliGovde = [ikiYuzFarkliGovdeMi](Cekirdek&) { return ikiYuzFarkliGovdeMi(); }; // ---- Montaj Kısıtları (CATIA/SolidWorks "mate" karşılığı) ---- // SEÇİLİ iki yüzden (farklı gövde) kisitCoz ile mafsal üretir, VAR OLAN // belge.mekanizma'ya (belge-başına tek örnek — kinematik ağaç paneliyle PAYLAŞILIR, // bkz. kinematik_agac.cpp) ekler. Uzuv yoksa (ilk kısıt) otomatik kurulur. const auto montajKisitUygula = [&sahne, &belge, &bilgiVer](montaj::KisitTip tip, f64 deger = 0.0, bool ters = false) { const auto& s = sahne.secililer(); if (s.size() != 2 || s[0].govde == s[1].govde || s[0].alt == 0 || s[1].alt == 0) { bilgiVer("Montaj kısıtı için farklı iki gövdeden birer yüz seçin"); return; } Dugum* da = sahne.bul(s[0].govde); Dugum* db = sahne.bul(s[1].govde); if (!da || !db || !da->brep || !db->brep || !da->brep->gecerliMi() || !db->brep->gecerliMi()) { bilgiVer("Montaj kısıtı: geçersiz gövde geometrisi"); return; } kinematik::Mekanizma& mek = belge.mekanizma; // Uzuv bul/oluştur: sahne kimliğine eşleşen mevcut uzuv, yoksa yeni ekle (taban = // gövdenin ŞU ANKİ dünya dönüşümü — mekanizma "sıfır" konumu, Uzuv doküman yorumu). // ad = d.ad AS-IS (boş bile olsa) — "Gövde" gibi sabit bir dolgu ADI VERMEK, birden // fazla adsız gövde uzuv olunca kinematik_agac.cpp'nin KENDİ index-bazlı disambiguation'ını // (boşsa "Uzuv.N") ezip aynı etiketi tekrarlatıyordu → ImGui aynı-ID çakışması (bulundu). const auto uzuvBulEkle = [&mek](Dugum& d) -> std::size_t { const auto& uz = mek.uzuvlar(); for (std::size_t i = 0; i < uz.size(); ++i) if (uz[i].sahne == d.kimlik) return i; return mek.uzuvEkle(d.ad, d.kimlik, d.donusum); }; const std::size_t uA = uzuvBulEkle(*da); const std::size_t uB = uzuvBulEkle(*db); // Dünya-uzayı B-Rep (kisit.hpp dosya başı SÖZLEŞMESİ: kisitCoz dünya bekler, // çağıran donusumUygula uygular — mafsal_oneri.hpp ile BİREBİR kalıp). const geometri::BrepGovde wa = geometri::donusumUygula(*da->brep, da->donusum); const geometri::BrepGovde wb = geometri::donusumUygula(*db->brep, db->donusum); montaj::Kisit k; k.ad = montaj::kisitTipAdi(tip); k.tip = tip; k.a = {s[0].govde, s[0].alt}; k.b = {s[1].govde, s[1].alt}; k.deger = deger; k.ters = ters; auto m = montaj::kisitCoz(k, wa, wb); if (!m) { bilgiVer("Bu iki yüz bu kısıt için uygun değil (ör. düzlem/silindir uyuşmazlığı)"); return; } // DÜNYA → YEREL: capa ANA uzvun, capaCocuk ÇOCUK uzvun taban'ının TERSİYLE // çevrilir (kisit.hpp / mafsal_oneri.hpp sözleşmesi). m->capa = mek.uzuvlar()[uA].taban.ters() * m->capa; m->capaCocuk = mek.uzuvlar()[uB].taban.ters() * m->capaCocuk; m->ana = uA; m->cocuk = uB; mek.mafsalEkle(*m); bilgiVer(std::string(montaj::kisitTipAdi(tip)) + " kısıtı eklendi (" + (da->ad.empty() ? "Gövde" : da->ad) + " ↔ " + (db->ad.empty() ? "Gövde" : db->ad) + ")"); }; komut("Montaj", "Sabit", "🔒", [montajKisitUygula](Cekirdek&) { montajKisitUygula(montaj::KisitTip::Sabit); }, "part", etkinIkiYuzFarkliGovde); komut("Montaj", "Eş Eksenli", "⌾", [montajKisitUygula](Cekirdek&) { montajKisitUygula(montaj::KisitTip::EsEksenli); }, "part", etkinIkiYuzFarkliGovde); komut("Montaj", "Çakışık", "▱", [montajKisitUygula](Cekirdek&) { montajKisitUygula(montaj::KisitTip::Cakisik); }, "part", etkinIkiYuzFarkliGovde); // Mesafe/Açı: sayısal değer gerekir → hemen uygulamak yerine küçük bir popup // aç (bglm.montajDeger* — bilgiVer/tumunuSigdir ile AYNI kalıp, frame döngüsüyle // paylaşılır). Popup render'ı aşağıda "Hızlı görünümler" popup'ının yanına eklendi. komut("Montaj", "Mesafe", "↔", [montajKisitUygula, &bglm](Cekirdek&) { bglm.montajDegerUygula = [montajKisitUygula](f64 d) { montajKisitUygula(montaj::KisitTip::Mesafe, d); }; bglm.montajDegerAci = false; bglm.montajDegerDeger = 0.0f; bglm.montajDegerAcik = true; }, "part", etkinIkiYuzFarkliGovde); komut("Montaj", "Açı", "∠", [montajKisitUygula, &bglm](Cekirdek&) { bglm.montajDegerUygula = [montajKisitUygula](f64 d) { montajKisitUygula(montaj::KisitTip::Aci, d); }; bglm.montajDegerAci = true; bglm.montajDegerDeger = 0.0f; bglm.montajDegerAcik = true; }, "part", etkinIkiYuzFarkliGovde); // ---- IJK Hedefine Gönder: seçili yüzün normalini / düz kenarın doğrultusunu // Kafa penceresinin (Diagonal/Ozmo) I/J/K hedef alanlarına taşır. bglm.bekleyenIjkHedef // AYNI "değer üye, referans DEĞİL" deseni (montajDeger* ile) — Kafa penceresi // kafaGuncelle karesinde tüketir (bkz. kafa_penceresi.cpp / uygulama_baglam.hpp). const auto tekYuzVeyaKenarMi = [&sahne]() -> bool { if (sahne.kip() != SecimKip::Yuz && sahne.kip() != SecimKip::Kenar) return false; const auto& s = sahne.secililer(); return s.size() == 1 && s[0].alt != 0; }; const auto etkinTekYuzVeyaKenar = [tekYuzVeyaKenarMi](Cekirdek&) { return tekYuzVeyaKenarMi(); }; komut("IJK", "Bu Yönü IJK Hedefine Gönder", "➜", [&sahne, &bglm](Cekirdek&) { const auto& s = sahne.secililer(); if (s.size() != 1 || s[0].alt == 0) return; Dugum* dd = sahne.bul(s[0].govde); if (!dd || !dd->brep) return; Vec3d yon; bool bulundu = false; if (sahne.kip() == SecimKip::Kenar) { geometri::KenarBilgi kb; if (dd->brep->kenarBilgi(s[0].alt, kb) && kb.dogruMu) { yon = dd->donusum.yonelim.dondur(kb.yon).birim(); bulundu = true; } } else { geometri::YuzBilgi yb; if (dd->brep->yuzBilgi(s[0].alt, yb) && yb.duzlemMi) { yon = dd->donusum.yonelim.dondur(yb.normal).birim(); bulundu = true; } } if (!bulundu) { bglm.bilgiVer("IJK hedefi için düz kenar veya düzlem yüz seçin"); return; } bglm.bekleyenIjkHedef = yon; bglm.bilgiVer("Yön Kafa penceresinin IJK hedefine gönderildi"); }, "part", etkinTekYuzVeyaKenar); // ---- Workbench'ler (CATIA tarzı çalışma ortamları) ---- // İlk eklenen otomatik aktif olur. Part Design = ana modelleme; Sketcher = 2D. cekirdek.workbenchler().ekle({"part", "Part Design", "⬡", nullptr, nullptr}); // Sketcher: alt şerit gizli (DM ile aynı minimal dil) — çizim araçları sağ-tık bağlam // menüsünde (sketchBaglamMenuCiz "Araç"/"Düzenle"). Çıkışta DM.giris zaten gizli tutar. cekirdek.workbenchler().ekle({"sketcher", "Sketcher", "✎", [&g_serit](Cekirdek&) { g_serit.gizle(); }, [&g_serit](Cekirdek&) { g_serit.goster(); }}); // CAM workbench: op'lar ANA ürün ağacında yaşar (camAgacTopla); ayrı "Üretim // Programı" penceresi kaldırıldı → girişte ekstra pencere açılmaz. cekirdek.workbenchler().ekle({"cam", "CAM", "⚙", nullptr, nullptr}); // GSD workbench: yüzey + tel-kafes modelleme (CATIA Generative Shape Design). cekirdek.workbenchler().ekle({"gsd", "Generative Shape Design", "▱", nullptr, nullptr}); // Analiz workbench: draft/undercut/kalınlık/radius + kesit (kalıp/döküm analiz). // cekirdek.workbenchler().ekle( // {"analiz", "Analiz", "🔬", [&analizSis](Cekirdek&) { analizSis.acik = true; }, nullptr}); // // Döküm Simülasyonu workbench: entalpi-FDM katılaşma (Niyama/hot-spot). // cekirdek.workbenchler().ekle( // {"dokum", "Döküm Simülasyonu", "🔥", [&dokumSis](Cekirdek&) { dokumSis.acik = true; }, nullptr}); // CAE / Simülasyon workbench: kinematik + DMU girişim analizi. Mekanizma şimdiye dek // yalnız Lua/MCP'den sürülebiliyordu; bu çatı onu klavyesiz açar. Analiz ve Döküm // workbench'leri DEĞİŞMEDİ (davranış birebir korunur) — CAE onların üstüne EK çatıdır. // cekirdek.workbenchler().ekle({"cae", "CAE / Simülasyon", "🧪", // [](Cekirdek&) { kinematikPaneliAcik() = true; }, // [](Cekirdek&) { kinematikPaneliAcik() = false; }}); // Doğrudan Modelleme workbench (Plasticity tarzı): girince alt şerit kalkar, seçili // yüzde tıkla-sürükle it/çek doğrudan çalışır (butonsuz). Çıkışta eski hâle döner. cekirdek.workbenchler().ekle( {"dogrudan", "Doğrudan Modelleme", "✥", [&ort, &g_serit, &sahne](Cekirdek&) { g_serit.gizle(); ort.pushPull.aktif = true; ort.pushPull.surukluyor = false; ort.kenarFillet.aktif = true; ort.kenarFillet.surukluyor = false; ort.sketchItCek.aktif = true; ort.sketchItCek.surukluyor = false; ort.featureReDuzenle.aktif = true; ort.featureReDuzenle.surukluyor = false; ort.yuzTasi.aktif = true; // tam transform gizmosu OTOMATİK (tek düz yüz seçilince belirir) ort.yuzTasi.surukluyor = false; ort.yuzTasi.hedefImza = 0; ort.kenarGizmo.aktif = true; // kenar seçilince ⟀/⇱ (kenar_gizmo.hpp) ort.kenarGizmo.hedefImza = 0; ort.pushPull.hedefImza = 0; // simetri: yuzTasi tazeleniyordu, push/pull unutulmuştu // Gizmolar SEÇİLİ yüz/kenardan doğar; Gövde/ağaç kipinde seçim (alt=0) gizmo // üretmez → girişte kipi Yüz'e çek (ilk tık doğrudan çalışsın, sezgisel). if (sahne.kip() != SecimKip::Yuz && sahne.kip() != SecimKip::Kenar) sahne.kipAyarla(SecimKip::Yuz); }, [&ort, &g_serit](Cekirdek&) { g_serit.goster(); ort.pushPull.aktif = false; ort.pushPull.surukluyor = false; ort.kenarFillet.aktif = false; ort.kenarFillet.surukluyor = false; ort.sketchItCek.aktif = false; ort.sketchItCek.surukluyor = false; ort.featureReDuzenle.aktif = false; ort.featureReDuzenle.surukluyor = false; ort.yuzTasi.aktif = false; ort.yuzTasi.surukluyor = false; ort.kenarGizmo.aktif = false; }}); // Açılışta DM aktif: modelleme için TEK ortam (part/sketcher/gsd DM içinde birleşti). // İlk-eklenen "part" yerine DM'e geç → şerit gizli, gesture + bağlam menüsü hazır. cekirdek.workbenchler().aktifAyarla(cekirdek, "dogrudan"); // TEK-WORKBENCH BOŞLUĞU: kinematik/DMU paneli yalnız CAE wb giriş kancasından // açılıyordu; seçici gizlenince erişim kaybolurdu (Tur 4 taraması). Komut olarak // kaydet → arama menüsü/paletten DM içinden açılır (wb'ye geçmeden). komut("Simülasyon", "Kinematik / DMU Paneli", "🧪", [](Cekirdek&) { kinematikPaneliAcik() = true; }); // "Yeni Belge" = YENİ SÜREÇ (yeni fikocad.exe örneği). Süreç izolasyonu doğal scope: // kendi aygıtı/belgesi/penceresi — paylaşımlı-GPU çok-pencere karmaşası kökten gider. komut("Dosya", "Yeni Belge", "🗎", [&](Cekirdek&) { fiko::yeniBelgeKomut(bglm); }); komut("Dosya", "Aç (.fikocad)...", "📂", [&](Cekirdek&) { fiko::fikoAc(bglm); }); komut("Dosya", "Kaydet (.fikocad)...", "💾", [&](Cekirdek&) { fiko::fikoKaydetKomut(bglm); }); komut("Dosya", "STEP İçe Aktar...", "", [&](Cekirdek&) { fiko::stepAc(bglm); }); komut("Dosya", "STEP Dışa Aktar...", "", [&](Cekirdek&) { fiko::stepKaydetKomut(bglm); }, "", etkinBrepli); komut("Dosya", "Dışa Aktar (STL/OBJ/IGES)...", "⭳", [&](Cekirdek&) { fiko::disaAktarKomut(bglm); }, "", etkinBrepli); komut("Dosya", "APT Aç...", "⭱", [&](Cekirdek&) { fiko::aptAc(bglm); }); komut("Dosya", "Web CAD Aç (.FikoOp/.wrl)...", "🌐", [&](Cekirdek&) { fiko::webAc(bglm); }); komut("Dosya", "APT Aktar...", "⭳", [&](Cekirdek&) { fiko::aptAktar(bglm); }, "", etkinBrepli); komut("Dosya", "Olay Günlüğü Yaz (.fikolog)", "", [&](Cekirdek& c) { c.olaylar().diskeYaz(olayLogYol); bilgiVer("Olay günlüğü yazıldı (.fikolog)"); }); // Geri Al / Yinele — etkinMi ile menüde durum-duyarlı; simge ile toolbar'da da. menuK.ekle({"Düzenle", "Geri Al", "↶", 0, [](Cekirdek& c) { c.geriAl(); }, [](Cekirdek& c) { return c.komutlar().geriAlinabilir(); }}); menuK.ekle({"Düzenle", "Yinele", "↷", 0, [](Cekirdek& c) { c.yinele(); }, [](Cekirdek& c) { return c.komutlar().yinelenebilir(); }}); const auto geoEkle = [&](DugumTip t) { cekirdek.uygula(std::make_unique(sahne, t)); }; komut("Geometri", "Kutu", "◼", [&, geoEkle](Cekirdek&) { geoEkle(DugumTip::Kutu); }, "part"); komut("Geometri", "Silindir", "▮", [&, geoEkle](Cekirdek&) { geoEkle(DugumTip::Silindir); }, "part"); komut("Geometri", "Küre", "●", [&, geoEkle](Cekirdek&) { geoEkle(DugumTip::Kure); }, "part"); komut("Geometri", "Koni", "▲", [&, geoEkle](Cekirdek&) { geoEkle(DugumTip::Koni); }, "part"); // DM tek-atış işlemini asyncApply açıkken WORKER'da koştur (devasa modelde UI donmaz): // op geometri fonksiyonu (girdi kopyası worker'da), undo+toast sonuç pompadan inince // kurulur. false dönerse (ayar kapalı) çağıran eski senkron yolunu izler — davranış birebir. const auto dmAsyncDirekt = [&sahne, &cekirdek](u32 g, std::function&)> op, const char* isMsg, const char* okMsg, const char* hataMsg) -> bool { if (!dogrudanAyar().asyncApply) return false; const DugumPusula eski = sahne.anlik(g); sahne.asyncDirektBaslat(g, std::move(op), [&sahne, &cekirdek, g, eski, okMsg, hataMsg](bool ok) { if (ok) { cekirdek.uygula(std::make_unique( sahne, eski, sahne.anlik(g))); ui::toastGoster(okMsg, ui::ToastTip::Basari); } else ui::toastGoster(hataMsg, ui::ToastTip::Hata); }); ui::toastGoster(isMsg, ui::ToastTip::Bilgi); return true; }; // Kabuk/Shell: seçili yüzler açıklık, gövde içi boşaltılır (mevcut geometri::kabuk). komut("Değiştir", "Kabuk", "⬚", [&, dmAsyncDirekt](Cekirdek& cek) { std::vector yuzler; u32 g = 0; for (const SecimOge& o : sahne.secililer()) { if (!g) g = o.govde; if (o.govde == g) yuzler.push_back(o.alt); } if (yuzler.empty() || !g) { ui::toastGoster("Kabuk için yüz seçin", ui::ToastTip::Uyari); return; } if (dmAsyncDirekt(g, [yuzler, kal = (f64)dogrudanAyar().kabukKalinlik]( const geometri::BrepGovde& b, std::vector&) { return geometri::kabuk(b, yuzler, kal); }, "Kabuk uygulanıyor…", "Kabuk uygulandı", "Kabuk başarısız")) return; const DugumPusula eski = sahne.anlik(g); if (sahne.kabukUygula(g, yuzler, dogrudanAyar().kabukKalinlik)) { cek.uygula(std::make_unique(sahne, eski, sahne.anlik(g))); ui::toastGoster("Kabuk uygulandı", ui::ToastTip::Basari); } else ui::toastGoster("Kabuk başarısız", ui::ToastTip::Hata); }, "part"); // Yuvarlat/Pah: seçili kenarlara fillet/chamfer (gizmo'ya ek menü + Command Palette erişimi). const auto kenarIslem = [&](Cekirdek& cek, bool yuvarlat) { u32 g = 0; for (const SecimOge& o : sahne.secililer()) { g = o.govde; break; } if (!g) { ui::toastGoster("Kenar seçin", ui::ToastTip::Uyari); return; } const DugumPusula eski = sahne.anlik(g); if (sahne.pahYuvarlatUygula(dogrudanAyar().filletYaricap, yuvarlat) > 0) { cek.uygula(std::make_unique(sahne, eski, sahne.anlik(g))); ui::toastGoster(yuvarlat ? "Yuvarlatıldı" : "Pah uygulandı", ui::ToastTip::Basari); } else ui::toastGoster("İşlem başarısız (kenar seçin)", ui::ToastTip::Hata); }; komut("Değiştir", "Yuvarlat", "◜", [&, kenarIslem](Cekirdek& cek) { kenarIslem(cek, true); }, "part"); komut("Değiştir", "Pah", "◺", [&, kenarIslem](Cekirdek& cek) { kenarIslem(cek, false); }, "part"); // Dairesel Dizi: seçili silindirik yüz = eksen, gövde döndürülüp çoğaltılır. komut("Değiştir", "Dairesel Dizi", "❋", [&, dmAsyncDirekt](Cekirdek& cek) { u32 g = 0, y = 0; for (const SecimOge& o : sahne.secililer()) { g = o.govde; y = o.alt; break; } if (!g || !y) { ui::toastGoster("Dizi ekseni için silindirik yüz seçin", ui::ToastTip::Uyari); return; } const u32 adet = (u32)(dogrudanAyar().diziAdet + 0.5f); // Eksen = seçili silindirik yüzden (MAIN'de çöz); geometri::diziDairesel worker'da. geometri::YuzBilgi yb; if (Dugum* dd = sahne.bul(g); dd && dd->brep && dd->brep->yuzBilgi(y, yb) && yb.silindirMi && dmAsyncDirekt( g, [adet, aci = (f64)dogrudanAyar().diziAci, nokta = yb.eksenNokta, eks = yb.eksen]( const geometri::BrepGovde& b, std::vector&) { return geometri::diziDairesel(b, nokta, eks, aci, adet); }, "Dairesel dizi uygulanıyor…", "Dairesel dizi uygulandı", "Dairesel dizi başarısız (silindirik yüz gerekli)")) return; const DugumPusula eski = sahne.anlik(g); if (sahne.diziDaireselUygula(g, y, adet, dogrudanAyar().diziAci)) { cek.uygula(std::make_unique(sahne, eski, sahne.anlik(g))); ui::toastGoster("Dairesel dizi uygulandı", ui::ToastTip::Basari); } else ui::toastGoster("Dairesel dizi başarısız (silindirik yüz gerekli)", ui::ToastTip::Hata); }, "part"); // Draft: seçili yüzlere kalıp çıkış açısı (çekme yönü = global Z MVP). komut("Değiştir", "Draft", "◿", [&, dmAsyncDirekt](Cekirdek& cek) { std::vector yuzler; u32 g = 0; for (const SecimOge& o : sahne.secililer()) { if (!g) g = o.govde; if (o.govde == g) yuzler.push_back(o.alt); } if (yuzler.empty() || !g) { ui::toastGoster("Draft için yüz seçin", ui::ToastTip::Uyari); return; } // Çekme yönü = ilk seçili düz yüzün normali (yoksa global Z geriye-uyum). Vec3d cekme{0, 0, 1}; if (Dugum* dd = sahne.bul(g); dd && dd->brep) { geometri::YuzBilgi yb; if (dd->brep->yuzBilgi(yuzler[0], yb) && yb.duzlemMi) cekme = yb.normal; } if (dmAsyncDirekt(g, [yuzler, aci = (f64)dogrudanAyar().draftAci, cekme]( const geometri::BrepGovde& b, std::vector&) { return geometri::yuzDraft(b, yuzler, aci, cekme, {}, {}, false); }, "Draft uygulanıyor…", "Draft uygulandı", "Draft başarısız")) return; const DugumPusula eski = sahne.anlik(g); if (sahne.draftUygula(g, yuzler, dogrudanAyar().draftAci, cekme)) { cek.uygula(std::make_unique(sahne, eski, sahne.anlik(g))); ui::toastGoster("Draft uygulandı", ui::ToastTip::Basari); } else ui::toastGoster("Draft başarısız", ui::ToastTip::Hata); }, "part"); // Parça Ayır: seçili yüzün ait olduğu ayrık katıyı tut, diğerlerini at. komut("Değiştir", "Parça Ayır", "⊟", [&](Cekirdek& cek) { std::vector yuzler; u32 g = 0; for (const SecimOge& o : sahne.secililer()) { if (!g) g = o.govde; if (o.govde == g) yuzler.push_back(o.alt); } if (yuzler.empty() || !g) { ui::toastGoster("Parça için yüz seçin", ui::ToastTip::Uyari); return; } const DugumPusula eski = sahne.anlik(g); if (sahne.lumpUygula(g, yuzler, true)) { cek.uygula(std::make_unique(sahne, eski, sahne.anlik(g))); ui::toastGoster("Parça ayrıldı", ui::ToastTip::Basari); } else ui::toastGoster("Parça ayır başarısız (tek parça olabilir)", ui::ToastTip::Hata); }, "part"); // Doğrusal Dizi: seçili düz yüz normali = yön, gövde aralıkla çoğaltılır. komut("Değiştir", "Doğrusal Dizi", "⋯", [&, dmAsyncDirekt](Cekirdek& cek) { u32 g = 0, y = 0; for (const SecimOge& o : sahne.secililer()) { g = o.govde; y = o.alt; break; } if (!g || !y) { ui::toastGoster("Dizi yönü için düz yüz seçin", ui::ToastTip::Uyari); return; } const u32 adet = (u32)(dogrudanAyar().diziAdet + 0.5f); // Yön = seçili düz yüz normali (MAIN'de çöz — sahne worker'a girmez); geometri::diziKonum // ile ofset dizisi. Yüz düz değilse async'e girmez → senkron yol net hata verir. geometri::YuzBilgi yb; if (Dugum* dd = sahne.bul(g); dd && dd->brep && dd->brep->yuzBilgi(y, yb) && yb.duzlemMi && dmAsyncDirekt( g, [adet, mes = (f64)dogrudanAyar().diziLinMesafe, n = yb.normal]( const geometri::BrepGovde& b, std::vector&) { std::vector ofsetler; ofsetler.reserve(adet); for (u32 i = 0; i < adet; ++i) ofsetler.push_back(n * (mes * (f64)i)); return geometri::diziKonum(b, ofsetler); }, "Doğrusal dizi uygulanıyor…", "Doğrusal dizi uygulandı", "Doğrusal dizi başarısız (düz yüz gerekli)")) return; const DugumPusula eski = sahne.anlik(g); if (sahne.diziLineerUygula(g, y, adet, dogrudanAyar().diziLinMesafe)) { cek.uygula(std::make_unique(sahne, eski, sahne.anlik(g))); ui::toastGoster("Doğrusal dizi uygulandı", ui::ToastTip::Basari); } else ui::toastGoster("Doğrusal dizi başarısız (düz yüz gerekli)", ui::ToastTip::Hata); }, "part"); // Ayna/Mirror: seçili düz yüz = ayna düzlemi, gövde aynalanıp birleştirilir. komut("Değiştir", "Ayna", "⧉", [&, dmAsyncDirekt](Cekirdek& cek) { u32 g = 0, y = 0; for (const SecimOge& o : sahne.secililer()) { g = o.govde; y = o.alt; break; } if (!g || !y) { ui::toastGoster("Ayna için düz yüz seçin", ui::ToastTip::Uyari); return; } // Ayna düzlemi = seçili düz yüz (MAIN'de çöz); aynala+birleşim worker'da (boolean ağır). geometri::YuzBilgi yb; if (Dugum* dd = sahne.bul(g); dd && dd->brep && dd->brep->yuzBilgi(y, yb) && yb.duzlemMi && dmAsyncDirekt( g, [merkez = yb.merkez, n = yb.normal](const geometri::BrepGovde& b, std::vector&) { geometri::BrepGovde ayna = geometri::aynala(b, merkez, n); if (!ayna.gecerliMi()) return geometri::BrepGovde{}; return geometri::birlesim(b, ayna); }, "Ayna uygulanıyor…", "Ayna uygulandı", "Ayna başarısız (düz yüz gerekli)")) return; const DugumPusula eski = sahne.anlik(g); if (sahne.aynalaUygula(g, y)) { cek.uygula(std::make_unique(sahne, eski, sahne.anlik(g))); ui::toastGoster("Ayna uygulandı", ui::ToastTip::Basari); } else ui::toastGoster("Ayna başarısız (düz yüz gerekli)", ui::ToastTip::Hata); }, "part"); // Kalınlık/Thickness: tüm gövde offset (mevcut geometri::kalinlastir). komut("Değiştir", "Kalınlık", "⊕", [&, dmAsyncDirekt](Cekirdek& cek) { u32 g = 0; for (const SecimOge& o : sahne.secililer()) { g = o.govde; break; } if (!g) { ui::toastGoster("Kalınlık için gövde seçin", ui::ToastTip::Uyari); return; } if (dmAsyncDirekt(g, [mes = (f64)dogrudanAyar().kalinlikMesafe](const geometri::BrepGovde& b, std::vector&) { return geometri::kalinlastir(b, mes); }, "Kalınlık uygulanıyor…", "Kalınlık uygulandı", "Kalınlık başarısız")) return; const DugumPusula eski = sahne.anlik(g); if (sahne.kalinlastirUygula(g, dogrudanAyar().kalinlikMesafe)) { cek.uygula(std::make_unique(sahne, eski, sahne.anlik(g))); ui::toastGoster("Kalınlık uygulandı", ui::ToastTip::Basari); } else ui::toastGoster("Kalınlık başarısız", ui::ToastTip::Hata); }, "part"); // Yüz Taşı (Move Face — Plasticity/CATIA): seçili DÜZ yüzü, seçili KENARIN yönünde (varsa; // eğik→duvarlar esner) yoksa yüz NORMALİ boyunca öteler (geometri::yuzTasi/BRepFeat_MakePrism). // Kenar yüz düzlemine paralelse (yüz sınırı kenarı) dejenere → normal fallback devreye girer. komut("Değiştir", "Yüz Taşı", "⇲", [&, dmAsyncDirekt](Cekirdek& cek) { u32 g = 0, yuzOrd = 0, kenarOrd = 0; // Yüz kipinde TÜM seçili düz yüzler birlikte taşınır (Plasticity multi-face move); başka // kipte eski ayrım korunur (ilk yüz = taşınacak, ilk kenar = yön). const bool yuzKipi = sahne.kip() == SecimKip::Yuz; std::vector yuzler; for (const SecimOge& o : sahne.secililer()) { if (o.alt == 0) continue; if (!g) g = o.govde; if (o.govde != g) continue; geometri::YuzBilgi yb; geometri::KenarBilgi kb; Dugum* dd = sahne.bul(o.govde); if (!dd || !dd->brep) continue; if (yuzKipi) { if (dd->brep->yuzBilgi(o.alt, yb) && yb.duzlemMi) yuzler.push_back(o.alt); // yalnız düz } else if (!yuzOrd && dd->brep->yuzBilgi(o.alt, yb)) yuzOrd = o.alt; // ilk yüz = taşınacak else if (!kenarOrd && dd->brep->kenarBilgi(o.alt, kb)) kenarOrd = o.alt; // ilk kenar = yön } if (yuzKipi && !yuzler.empty()) yuzOrd = yuzler.front(); // yön/düzlem referansı = ilk yüz else if (yuzOrd) yuzler.assign(1, yuzOrd); if (!g || !yuzOrd) { ui::toastGoster("Yüz Taşı için düz yüz seçin", ui::ToastTip::Uyari); return; } // İnteraktif gizmo (varsayılan): 3-eksen tutamacı aç → kullanıcı yönü/mesafeyi sürükleyerek // belirler. Kapalıysa aşağıdaki sabit-mesafe tek-atış yolu (eski davranış korunur). if (dogrudanAyar().yuzTasiGizmo) { ort.yuzTasi.aktif = true; ort.yuzTasi.surukluyor = false; ort.yuzTasi.hedefImza = 0; // seçili yüzü hemen örnekle if (sahne.kip() != SecimKip::Yuz) sahne.kipAyarla(SecimKip::Yuz); ui::toastGoster("Yüz Taşı: eksen okunu sürükle (sayı yaz = hassas, Esc = kapat)", ui::ToastTip::Bilgi); return; } Dugum* dd = sahne.bul(g); geometri::YuzBilgi yb; if (!dd || !dd->brep || !dd->brep->yuzBilgi(yuzOrd, yb) || !yb.duzlemMi) { ui::toastGoster("Yüz Taşı yalnız düz yüzde çalışır", ui::ToastTip::Uyari); return; } Vec3d yon = yb.normal; // varsayılan: yüz normali (saf öteleme — her zaman geçerli) geometri::KenarBilgi kb; if (kenarOrd && dd->brep->kenarBilgi(kenarOrd, kb) && kb.dogruMu) { const Vec3d ky = kb.yon.birim(); // Kenar yönü yüze paralel değilse (normal bileşen var) onu kullan; paralel ise normal kalır. if (std::abs(ky.nokta(yb.normal)) > 1e-4) yon = ky; } const Vec3d vec = yon.birim() * (f64)dogrudanAyar().tasiMesafe; // RE-EDITABLE FEATURE (yuz_tasi): ürün ağacında düzenlenebilir (tasiX/Y/Z) — one-shot // direct-op yerine (CATIA paritesi). Async worker'da (devasa modelde donmaz) + provenance. const u32 fk = partD.featureBaslat("yuz_tasi", VARLIK_YOK, g); if (!fk) { ui::toastGoster("Yüz Taşı başarısız (katı gövde gerek)", ui::ToastTip::Hata); return; } // Çok seçim: yuzSayi + yuz0..N (yuz_itcek kalıbı) → tek vektör TÜM seçili yüzlere. std::vector> params{ {"tasiX", vec.x}, {"tasiY", vec.y}, {"tasiZ", vec.z}, {"yuzSayi", (f64)yuzler.size()}}; for (std::size_t i = 0; i < yuzler.size(); ++i) params.push_back({"yuz" + std::to_string(i), (f64)yuzler[i]}); const bool cok = yuzler.size() > 1; if (dogrudanAyar().asyncApply && partD.featureAsyncUygula(fk, params, [&cekirdek, &partD, cok, fk](bool ok) { if (ok) dmFeatureEkleUndo(cekirdek, partD, fk); ui::toastGoster(ok ? (cok ? "Yüzler taşındı" : "Yüz taşındı") : "Yüz Taşı başarısız (yön yüze paralel olabilir)", ok ? ui::ToastTip::Basari : ui::ToastTip::Hata); })) { ui::toastGoster(cok ? "Yüzler taşınıyor…" : "Yüz taşınıyor…", ui::ToastTip::Bilgi); return; } partD.featureParamToplu(fk, params); if (partD.featureOnayla(fk)) dmFeatureEkleUndo(cekirdek, partD, fk); ui::toastGoster("Yüz taşındı", ui::ToastTip::Basari); }, "part"); // Kenardan Eğ (Fikri 07-22 DM): seçili DÜZ kenar MENTEŞE — komşu DÜZ yüz o kenar // etrafında eğilir (kenar_eg feature → kenardanYuzEg/DraftAngle). Yüz seçimi: kenarla // birlikte komşu yüz de seçiliyse o; yoksa komşulardan İLK DÜZ olan. Açı = ayar // varsayılanı; re-editable feature → özellik panelinden değiştirilir (yuz_tasi kalıbı). komut("Değiştir", "Kenardan Eğ", "⟀", [&](Cekirdek&) { u32 g = 0, ke = 0; for (const SecimOge& o : sahne.secililer()) if (o.alt) { g = o.govde; ke = o.alt; break; } Dugum* d = g ? sahne.bul(g) : nullptr; if (!d || !d->brep || !ke) { ui::toastGoster("Kenardan Eğ: kenar kipinde menteşe kenarını seçin", ui::ToastTip::Uyari); return; } geometri::KenarBilgi kb; if (!d->brep->kenarBilgi(ke, kb) || !kb.dogruMu) { ui::toastGoster("Kenardan Eğ: DÜZ kenar gerekli", ui::ToastTip::Uyari); return; } const std::vector komsu = d->brep->kenarKomsuYuzler(ke); u32 yuz = 0; for (const SecimOge& o : sahne.secililer()) // kenar+yüz çift seçimi varsa onu al if (o.govde == g && o.alt && o.alt != ke && !yuz) for (u32 ky : komsu) if (ky == o.alt) { yuz = o.alt; break; } if (!yuz) for (u32 ky : komsu) { geometri::YuzBilgi yb; if (d->brep->yuzBilgi(ky, yb, false) && yb.duzlemMi) { yuz = ky; break; } } if (!yuz) { ui::toastGoster("Kenardan Eğ: kenara komşu DÜZ yüz bulunamadı", ui::ToastTip::Uyari); return; } const u32 fk = partD.featureBaslat("kenar_eg", VARLIK_YOK, g); if (!fk) { ui::toastGoster("Kenardan Eğ başarısız (katı gövde gerek)", ui::ToastTip::Hata); return; } const std::vector> params{ {"kenar0", (f64)ke}, {"yuz0", (f64)yuz}, {"aci", (f64)dogrudanAyar().kenarEgAci}}; if (dogrudanAyar().asyncApply && partD.featureAsyncUygula(fk, params, [&cekirdek, &partD, fk](bool ok) { if (ok) dmFeatureEkleUndo(cekirdek, partD, fk); ui::toastGoster(ok ? "Kenardan eğildi — açı özellik panelinden" : "Kenardan Eğ başarısız (nedeni ürün ağacında)", ok ? ui::ToastTip::Basari : ui::ToastTip::Hata); })) { ui::toastGoster("Kenardan eğiliyor…", ui::ToastTip::Bilgi); dmTutamacOlayiYay(cekirdek, "Değiştir", "Kenardan Eğ"); return; } partD.featureParamToplu(fk, params); if (partD.featureOnayla(fk)) dmFeatureEkleUndo(cekirdek, partD, fk); dmTutamacOlayiYay(cekirdek, "Değiştir", "Kenardan Eğ"); ui::toastGoster("Kenardan eğildi — açı özellik panelinden", ui::ToastTip::Basari); }, "part"); // Kenardan Uzat (Fikri 07-22 DM "uzatma"): kenar_uzat_duzlemde AÇIK (varsayılan) → // kenar_tasi feature: SADECE çekilen kenar hareket eder (seçili yüz düzleminde uzar, // öteki komşu uzak menteşesinden eğilir). KAPALI veya kenar eğriyse → eski davranış: // komşu DÜZ yüz normali boyunca komple itilir (yuz_itcek). Yüz seçimi Kenardan Eğ // kalıbı (çift seçim varsa o; yoksa ilk düz komşu). Mesafe = dogrudan.tasi_mesafe ayarı; // re-editable feature → panelden değiştirilir. komut("Değiştir", "Kenardan Uzat", "⇱", [&](Cekirdek&) { u32 g = 0, ke = 0; for (const SecimOge& o : sahne.secililer()) if (o.alt) { g = o.govde; ke = o.alt; break; } Dugum* d = g ? sahne.bul(g) : nullptr; if (!d || !d->brep || !ke) { ui::toastGoster("Kenardan Uzat: kenar kipinde bir kenar seçin", ui::ToastTip::Uyari); return; } const std::vector komsu = d->brep->kenarKomsuYuzler(ke); u32 yuz = 0; for (const SecimOge& o : sahne.secililer()) if (o.govde == g && o.alt && o.alt != ke && !yuz) for (u32 ky : komsu) if (ky == o.alt) { yuz = o.alt; break; } if (!yuz) for (u32 ky : komsu) { geometri::YuzBilgi yb; if (d->brep->yuzBilgi(ky, yb, false) && yb.duzlemMi) { yuz = ky; break; } } if (!yuz) { ui::toastGoster("Kenardan Uzat: kenara komşu DÜZ yüz bulunamadı", ui::ToastTip::Uyari); return; } std::string tip = "yuz_itcek"; std::vector> params{ {"yuzSayi", 1.0}, {"yuz0", (f64)yuz}, {"mesafe", (f64)dogrudanAyar().tasiMesafe}}; if (dogrudanAyar().kenarUzatDuzlemde) { geometri::KenarBilgi kb; if (d->brep->kenarBilgi(ke, kb) && kb.dogruMu) { tip = "kenar_tasi"; params = {{"kenar0", (f64)ke}, {"yuz0", (f64)yuz}, {"mesafe", (f64)dogrudanAyar().tasiMesafe}}; } } const u32 fk = partD.featureBaslat(tip, VARLIK_YOK, g); if (!fk) { ui::toastGoster("Kenardan Uzat başarısız (katı gövde gerek)", ui::ToastTip::Hata); return; } if (dogrudanAyar().asyncApply && partD.featureAsyncUygula(fk, params, [&cekirdek, &partD, fk](bool ok) { if (ok) dmFeatureEkleUndo(cekirdek, partD, fk); ui::toastGoster(ok ? "Kenardan uzatıldı — mesafe özellik panelinden" : "Kenardan Uzat başarısız", ok ? ui::ToastTip::Basari : ui::ToastTip::Hata); })) { ui::toastGoster("Uzatılıyor…", ui::ToastTip::Bilgi); dmTutamacOlayiYay(cekirdek, "Değiştir", "Kenardan Uzat"); return; } partD.featureParamToplu(fk, params); if (partD.featureOnayla(fk)) dmFeatureEkleUndo(cekirdek, partD, fk); dmTutamacOlayiYay(cekirdek, "Değiştir", "Kenardan Uzat"); ui::toastGoster("Kenardan uzatıldı — mesafe özellik panelinden", ui::ToastTip::Basari); }, "part"); // Blok Kaydır (Move Region): seçili çıkıntı ÜST yüzü → taban düzlemi OTOMATİK bulunur; çıkıntı // taban boyunca RİJİT kaydırılır (cikinti_kaydir feature — Splitter+Fuse, footprint iyileşir). // Yön = seçili KENAR taban-düzlemine izdüşürülür (yoksa taban U teğeti), mesafe = ayar. komut("Değiştir", "Blok Kaydır", "⇄", [&](Cekirdek& cek) { (void)cek; u32 g = 0, yuzOrd = 0, kenarOrd = 0; for (const SecimOge& o : sahne.secililer()) { if (o.alt == 0) continue; if (!g) g = o.govde; if (o.govde != g) continue; geometri::YuzBilgi yb; geometri::KenarBilgi kb; Dugum* dd = sahne.bul(o.govde); if (!dd || !dd->brep) continue; if (!yuzOrd && dd->brep->yuzBilgi(o.alt, yb)) yuzOrd = o.alt; else if (!kenarOrd && dd->brep->kenarBilgi(o.alt, kb)) kenarOrd = o.alt; } if (!g || !yuzOrd) { ui::toastGoster("Blok Kaydır için çıkıntının düz üst yüzünü seçin", ui::ToastTip::Uyari); return; } Dugum* dd = sahne.bul(g); if (!dd || !dd->brep) return; const geometri::CikintiTabanSonuc ts = geometri::cikintiTabanBul(*dd->brep, yuzOrd, /*tamGovde*/ false); if (!ts.gecerli) { ui::toastGoster("Taban düzlemi bulunamadı (düz üst yüzlü çıkıntı gerekir)", ui::ToastTip::Uyari); return; } const Vec3d nW = dd->donusum.yonelim.dondur(ts.normal).birim(); // dünya taban normali Vec3d dir; geometri::KenarBilgi kb; if (kenarOrd && dd->brep->kenarBilgi(kenarOrd, kb) && kb.dogruMu) dir = dd->donusum.yonelim.dondur(kb.yon); else { const Vec3d ref = std::abs(nW.x) < 0.9 ? Vec3d{1, 0, 0} : Vec3d{0, 1, 0}; dir = nW.capraz(ref); } dir = dir - nW * dir.nokta(nW); // taban düzlemine izdüşür → teğet kaydırma (çıkıntı tabanda kalır) if (dir.uzunlukKare() < 1e-9) { ui::toastGoster("Kaydırma yönü taban düzlemine dik olamaz", ui::ToastTip::Uyari); return; } const Vec3d vec = dir.birim() * (f64)dogrudanAyar().blokKaydirMesafe; const u32 fk = partD.featureBaslat("cikinti_kaydir", VARLIK_YOK, g); if (!fk) { ui::toastGoster("Blok Kaydır başarısız (kaynamış katı gövde gerek)", ui::ToastTip::Hata); return; } std::vector> params{ {"yuz0", (f64)yuzOrd}, {"tasiX", vec.x}, {"tasiY", vec.y}, {"tasiZ", vec.z}}; // Async: taban arama + Splitter+Fuse worker'da (devasa modelde donmaz). Taban parametre // olarak GEÇİLMEZ — buradaki ts YEREL brep'ten, feature ise DÜNYA brep'inde çalışır // (dönüşümlü gövdede frame uyuşmazlığı olurdu); worker senkron yolla aynı ölçütle bulur. if (dogrudanAyar().asyncApply && partD.featureAsyncUygula(fk, params, [&cekirdek, &partD, fk](bool ok) { if (ok) dmFeatureEkleUndo(cekirdek, partD, fk); ui::toastGoster(ok ? "Blok kaydırıldı (ağaçtan yön/mesafe düzenlenebilir)" : "Blok kaydır başarısız (çıkıntı taban dışına taşmış olabilir)", ok ? ui::ToastTip::Basari : ui::ToastTip::Hata); })) { ui::toastGoster("Blok kaydırılıyor…", ui::ToastTip::Bilgi); return; } partD.featureParamToplu(fk, params); if (partD.featureOnayla(fk)) dmFeatureEkleUndo(cekirdek, partD, fk); ui::toastGoster("Blok kaydırıldı (ağaçtan yön/mesafe düzenlenebilir)", ui::ToastTip::Basari); }, "part"); // Kesit (Split): seçili DÜZ yüzün düzlemi kesici olur → gövde o düzlemden bölünür, yüz // normalinin BAKTIĞI yarı atılır (kesit düzlemi normali = bakış yönü). Parametrik feature: // ağaçtan düzlem kaynağı (Seçili / Dünya XY-YZ-ZX) + "Diğer Taraf" değiştirilebilir. komut("Değiştir", "Kesit", "◧", [&](Cekirdek&) { u32 g = 0, yuzOrd = 0; for (const SecimOge& o : sahne.secililer()) { if (o.alt == 0) continue; Dugum* dd = sahne.bul(o.govde); geometri::YuzBilgi yb; if (!dd || !dd->brep || !dd->brep->yuzBilgi(o.alt, yb) || !yb.duzlemMi) continue; g = o.govde; yuzOrd = o.alt; break; // ilk düz yüz = kesici düzlem } if (!g || !yuzOrd) { ui::toastGoster("Kesit için düz bir yüz seçin (düzlemi kesici olur)", ui::ToastTip::Uyari); return; } Dugum* dd = sahne.bul(g); geometri::YuzBilgi yb; if (!dd || !dd->brep || !dd->brep->yuzBilgi(yuzOrd, yb)) return; // Kesici düzlem DÜNYA'da (feature hedefDunyaBrep üstünde çalışır — yerel geçmek frame hatası). const Vec3d pW = dd->donusum.noktaUygula(yb.merkez); const Vec3d nW = dd->donusum.yonelim.dondur(yb.normal).birim(); const u32 fk = partD.featureBaslat("split", VARLIK_YOK, g); if (!fk) { ui::toastGoster("Kesit başarısız (katı gövde gerek)", ui::ToastTip::Hata); return; } std::vector> params{ {"duzlemKaynak", 0.0}, // 0 = seçili düzlem/yüz {"kesicix", pW.x}, {"kesiciy", pW.y}, {"kesiciz", pW.z}, {"kesiciNx", nW.x}, {"kesiciNy", nW.y}, {"kesiciNz", nW.z}, {"kesiciVar", 1.0}, {"tersYon", 0.0}}; if (dogrudanAyar().asyncApply && partD.featureAsyncUygula(fk, params, [&cekirdek, &partD, fk](bool ok) { if (ok) dmFeatureEkleUndo(cekirdek, partD, fk); ui::toastGoster(ok ? "Kesit alındı (ağaçtan düzlem/taraf değiştirilebilir)" : "Kesit başarısız (düzlem gövdeyi kesmiyor olabilir)", ok ? ui::ToastTip::Basari : ui::ToastTip::Hata); })) { ui::toastGoster("Kesit alınıyor…", ui::ToastTip::Bilgi); return; } partD.featureParamToplu(fk, params); if (partD.featureOnayla(fk)) dmFeatureEkleUndo(cekirdek, partD, fk); ui::toastGoster("Kesit alındı (ağaçtan düzlem/taraf değiştirilebilir)", ui::ToastTip::Basari); }, "part"); // CATIA "Insert > Body": yeni boş PartBody + AKTİF yap (In Work Object). Sonraki sketch/feature/ // import bu body'ye gider — her body ayrı çizim+feature kapsamı (scope). Undo: TODO (grupEkle). komut("Ekle", "Body", "⬡", [&](Cekirdek&) { const Varlik b = sahne.yeniBody(); ui::toastGoster("Yeni Body eklendi — In Work Object (sonraki feature'lar buraya)", ui::ToastTip::Basari); (void)b; }, "part"); // Sketch-on-Surface: seçili sketch profilini seçili EĞRİ yüze sarıp kabart (fuse) / oy (cut). // Hedef = seçili öğelerden ilk yüz (alt!=0); sketch = aktif/seçili sketch. DuzenleIslem undo. const auto embossIslem = [&aktifSketch, &sahne](Cekirdek& cek, bool fuse) { const Varlik sk = aktifSketch != VARLIK_YOK ? aktifSketch : sahne.seciliSketch(); if (sk == VARLIK_YOK) { ui::toastGoster("Önce bir sketch seçin/açın", ui::ToastTip::Uyari); return; } u32 g = 0, yuzOrd = 0; for (const SecimOge& o : sahne.secililer()) if (o.alt != 0) { g = o.govde; yuzOrd = o.alt; break; } // ilk yüz ögesi = hedef if (!g || !yuzOrd) { ui::toastGoster("Hedef (eğri) yüz seçin", ui::ToastTip::Uyari); return; } const DugumPusula eski = sahne.anlik(g); if (sahne.sketchYuzeKabartUygula(sk, g, yuzOrd, dogrudanAyar().embossMesafe, fuse)) { cek.uygula(std::make_unique(sahne, eski, sahne.anlik(g))); ui::toastGoster(fuse ? "Kabartma uygulandı" : "Oyma uygulandı", ui::ToastTip::Basari); } else ui::toastGoster("Emboss başarısız (eğri yüz + kapalı sketch gerek)", ui::ToastTip::Hata); }; komut("Değiştir", "Sketch Kabart", "⬈", [&, embossIslem](Cekirdek& cek) { embossIslem(cek, true); }, "part"); komut("Değiştir", "Sketch Oy", "⬊", [&, embossIslem](Cekirdek& cek) { embossIslem(cek, false); }, "part"); // Sketch → Katı (düz yüz Pad/Cep): DÜZLEM sketch'i o düzlemin normali boyunca prizma edip // seçili gövde ile birleştir (Pad) / çıkar (Cep). Hedef = seçili ilk gövde; sketch = aktif/seçili. const auto sketchKatiIslem = [&aktifSketch, &sahne](Cekirdek& cek, bool kes) { const Varlik sk = aktifSketch != VARLIK_YOK ? aktifSketch : sahne.seciliSketch(); if (sk == VARLIK_YOK) { ui::toastGoster("Önce bir sketch seçin/açın", ui::ToastTip::Uyari); return; } u32 g = 0; for (const SecimOge& o : sahne.secililer()) { g = o.govde; break; } // ilk seçili gövde = hedef if (!g) { ui::toastGoster("Hedef gövde seçin", ui::ToastTip::Uyari); return; } const DugumPusula eski = sahne.anlik(g); if (sahne.sketchKatiUygula(sk, g, dogrudanAyar().itCekMesafe, kes)) { cek.uygula(std::make_unique(sahne, eski, sahne.anlik(g))); ui::toastGoster(kes ? "Cep uygulandı" : "Pad uygulandı", ui::ToastTip::Basari); } else ui::toastGoster("Sketch Katı başarısız (düz yüz + kapalı sketch gerek)", ui::ToastTip::Hata); }; komut("Değiştir", "Sketch Pad", "⬆", [&, sketchKatiIslem](Cekirdek& c) { sketchKatiIslem(c, false); }, "part"); komut("Değiştir", "Sketch Cep", "⬇", [&, sketchKatiIslem](Cekirdek& c) { sketchKatiIslem(c, true); }, "part"); // Yüz Kaldır (CATIA Remove Face): seçili yüz(ler)i sil + komşuları uzatarak boşluğu kapat. // RE-EDITABLE FEATURE (remove_face): DuzenleIslem yerine ürün ağacına PARAMETRİK düğüm koyar // (yuz_tasi/blok kaydır kalıbı — çift-tık düzenle, suppress/rollback). Async worker (devasa // modelde donmaz) + provenance/renk koru. Fillet/pah/boss/delik feature'ı geri alır (Defeaturing). komut("Değiştir", "Yüz Kaldır", "⊘", [&](Cekirdek&) { std::vector yuzler; u32 g = 0; for (const SecimOge& o : sahne.secililer()) { if (o.alt == 0) continue; // yüz ögesi değil (gövde) if (!g) g = o.govde; if (o.govde == g) yuzler.push_back(o.alt); } if (yuzler.empty() || !g) { ui::toastGoster("Yüz Kaldır için yüz seçin", ui::ToastTip::Uyari); return; } const u32 fk = partD.featureBaslat("remove_face", VARLIK_YOK, g); if (!fk) { ui::toastGoster("Yüz Kaldır başarısız (katı gövde gerek)", ui::ToastTip::Hata); return; } std::vector> params{{"yuzSayi", (f64)yuzler.size()}}; for (size_t i = 0; i < yuzler.size(); ++i) params.emplace_back("yuz" + std::to_string(i), (f64)yuzler[i]); if (dogrudanAyar().asyncApply) { // Viewport ghost: kaldırılan yüzlerin dünya-merkezinde "işleniyor…" etiketi + ✕ iptal. Vec3f gMerkez{0, 0, 0}; int gn = 0; if (Dugum* dd = sahne.bul(g)) { geometri::YuzBilgi yb; for (u32 y : yuzler) if (dd->brep && dd->brep->yuzBilgi(y, yb)) { const Vec3d w = dd->donusum.noktaUygula(yb.merkez); gMerkez += Vec3f{(f32)w.x, (f32)w.y, (f32)w.z}; ++gn; } } if (gn) gMerkez = gMerkez * (1.0f / (f32)gn); const u32 ghostId = ort.yuzGhost.ekle(fk, gMerkez, "⊘ Yüz kaldırılıyor…"); if (partD.featureAsyncUygula(fk, params, [&ort, &cekirdek, &partD, ghostId, fk](bool ok) { ort.yuzGhost.sil(ghostId); // sonuç indi → ghost kalkar if (ok) dmFeatureEkleUndo(cekirdek, partD, fk); ui::toastGoster(ok ? "Yüz kaldırıldı" : "Yüz Kaldır başarısız (komşular kapanamadı)", ok ? ui::ToastTip::Basari : ui::ToastTip::Hata); })) return; // ghost gösteriliyor (× ile iptal edilebilir) ort.yuzGhost.sil(ghostId); // async başlamadı → ghost'u geri al, senkron yola düş } partD.featureParamToplu(fk, params); if (partD.featureOnayla(fk)) dmFeatureEkleUndo(cekirdek, partD, fk); ui::toastGoster("Yüz kaldırıldı", ui::ToastTip::Basari); }, "part"); // Yüz Sil (Delete Face): seçili yüz(ler)i sil, deliği ONARMADAN açık bırak → katı, açık yüzey // gövdesine döner (Yüz Kaldır'ın aksine komşu uzatmaz). Açık shell doluMu() değil → sync yol // (async worker solid şartı koşar). DuzenleIslem undo. kabuktanKati ile tekrar kapatılabilir. komut("Değiştir", "Yüz Sil", "⊗", [&](Cekirdek& cek) { std::vector yuzler; u32 g = 0; for (const SecimOge& o : sahne.secililer()) { if (o.alt == 0) continue; // yüz ögesi değil (gövde) if (!g) g = o.govde; if (o.govde == g) yuzler.push_back(o.alt); } if (yuzler.empty() || !g) { ui::toastGoster("Yüz Sil için yüz seçin", ui::ToastTip::Uyari); return; } const DugumPusula eski = sahne.anlik(g); if (sahne.yuzuSilUygula(g, yuzler)) { cek.uygula(std::make_unique(sahne, eski, sahne.anlik(g))); ui::toastGoster("Yüz silindi (açık yüzey gövdesi)", ui::ToastTip::Basari); } else ui::toastGoster("Yüz Sil başarısız", ui::ToastTip::Hata); }, "part"); // Yüz Kopyala (Plasticity Copy / CATIA Extract): seçili yüz(ler)i BAĞIMSIZ açık yüzey // (GSD Yuzey) gövdesine çıkar. Kaynak katı DEĞİŞMEZ → hibrit modelleme (katıdan yüzey türet). komut("Değiştir", "Yüz Kopyala", "⧉", [&](Cekirdek&) { std::vector yuzler; u32 g = 0; for (const SecimOge& o : sahne.secililer()) { if (o.alt == 0) continue; // yüz ögesi değil (gövde) if (!g) g = o.govde; if (o.govde == g) yuzler.push_back(o.alt); } if (yuzler.empty() || !g) { ui::toastGoster("Yüz Kopyala için yüz seçin", ui::ToastTip::Uyari); return; } if (sahne.yuzKopyalaUygula(g, yuzler)) ui::toastGoster("Yüz(ler) yüzey gövdesine kopyalandı", ui::ToastTip::Basari); else ui::toastGoster("Yüz Kopyala başarısız", ui::ToastTip::Hata); }, "part"); // Yüzey Kalınlaştır (Thicken Surface → Solid): seçili açık yüzey (GSD Yuzey) gövdesini // kalınlaştırıp YENİ katı ekler. Hibrit: yüz kopyala → yüzey → kalınlaştır → katı. komut("Değiştir", "Yüzey Kalınlaştır", "⬛", [&](Cekirdek&) { u32 g = 0; for (const SecimOge& o : sahne.secililer()) { g = o.govde; break; } // ilk seçili gövde if (!g) { ui::toastGoster("Yüzey Kalınlaştır için yüzey seçin", ui::ToastTip::Uyari); return; } if (sahne.yuzeyKatilastirUygula(g, dogrudanAyar().kalinlikMesafe)) ui::toastGoster("Yüzey katıya kalınlaştırıldı", ui::ToastTip::Basari); else ui::toastGoster("Yüzey Kalınlaştır başarısız (açık yüzey gerekir)", ui::ToastTip::Hata); }, "part"); // Kabuktan Katı (Make Solid): seçili KAPALI GSD yüzeyi katıya çevirir (hacmi doldurur). komut("Değiştir", "Kabuktan Katı", "◲", [&](Cekirdek&) { u32 g = 0; for (const SecimOge& o : sahne.secililer()) { g = o.govde; break; } if (!g) { ui::toastGoster("Kabuktan Katı için kapalı yüzey seçin", ui::ToastTip::Uyari); return; } if (sahne.kabuktanKatiUygula(g)) ui::toastGoster("Kapalı yüzey katıya çevrildi", ui::ToastTip::Basari); else ui::toastGoster("Kabuktan Katı başarısız (kapalı yüzey gerekir)", ui::ToastTip::Hata); }, "part"); // Yüzey Offset (paralel yüzey): seçili açık yüzey gövdesini ötele → yeni paralel referans yüzey. komut("Değiştir", "Yüzey Offset", "⧉", [&](Cekirdek&) { u32 g = 0; for (const SecimOge& o : sahne.secililer()) { g = o.govde; break; } if (!g) { ui::toastGoster("Yüzey Offset için yüzey seçin", ui::ToastTip::Uyari); return; } if (sahne.yuzeyOfsetUygula(g, dogrudanAyar().kalinlikMesafe)) ui::toastGoster("Paralel yüzey oluşturuldu", ui::ToastTip::Basari); else ui::toastGoster("Yüzey Offset başarısız (açık yüzey gerekir)", ui::ToastTip::Hata); }, "part"); // Yüzey ile Böl (Split Solid by Surface): seçili KATI'yı seçili GSD yüzey ile böler → parçalar // + orijinal gizlenir (geri alınabilir). Katı + yüzey birlikte seçilmeli (Ctrl+tık). komut("Değiştir", "Yüzey ile Böl", "⑃", [&](Cekirdek&) { u32 kati = 0, yuz = 0; for (const SecimOge& o : sahne.secililer()) { Dugum* d = sahne.bul(o.govde); if (!d) continue; if (d->tip == DugumTip::Yuzey) yuz = o.govde; else if (!kati) kati = o.govde; } if (!kati || !yuz) { ui::toastGoster("Katı + bölme yüzeyi seçin (Ctrl+tık)", ui::ToastTip::Uyari); return; } const int n = sahne.yuzeyIleBolUygula(kati, yuz); if (n >= 2) ui::toastGoster(("Katı " + std::to_string(n) + " parçaya bölündü").c_str(), ui::ToastTip::Basari); else ui::toastGoster("Yüzey ile Böl başarısız (yüzey katıyı kesmiyor?)", ui::ToastTip::Hata); }, "part"); // Yüz İt/Çek (numeric push/pull): seçili yüzü normali boyunca TAM mesafe (ayar) öteler — // gizmo drag yerine hassas değer. Düz/silindir/küre/koni (yuzItCek). DuzenleIslem undo. komut("Değiştir", "Yüz İt/Çek", "⇕", [&](Cekirdek& cek) { u32 g = 0, yuzOrd = 0; for (const SecimOge& o : sahne.secililer()) if (o.alt != 0) { g = o.govde; yuzOrd = o.alt; break; } // ilk yüz ögesi if (!g || !yuzOrd) { ui::toastGoster("Yüz İt/Çek için yüz seçin", ui::ToastTip::Uyari); return; } const DugumPusula eski = sahne.anlik(g); if (sahne.pushPullUygula(g, yuzOrd, dogrudanAyar().itCekMesafe)) { cek.uygula(std::make_unique(sahne, eski, sahne.anlik(g))); ui::toastGoster("Yüz it/çek uygulandı", ui::ToastTip::Basari); } else ui::toastGoster("Yüz it/çek başarısız (geçersiz geometri)", ui::ToastTip::Hata); }, "part"); komut("Boolean", "Birleştir", "∪", [&](Cekirdek&) { boolIslem(DugumTip::Birlesim); }, "part", etkinIkiGovde); komut("Boolean", "Çıkar (A−B)", "∖", [&](Cekirdek&) { boolIslem(DugumTip::Cikarma); }, "part", etkinIkiGovde); komut("Boolean", "Kesişim", "∩", [&](Cekirdek&) { boolIslem(DugumTip::Kesisim); }, "part", etkinIkiGovde); // WTEC Açılı Kafa mini-viewport paneli (üstte 3D kafa + altta B/C açı kontrolleri). // ---- Akıllı seçim (motor brep.hpp'deki hazır yayılımlar → seçime köprü) ---- // Sağ-tık arama menüsü / radyal menü / komut paletinde çıkar → SIFIR klavye kombinasyonu. { struct AsKayit { const char* ad; const char* simge; AkilliSecim tur; }; static const AsKayit kAs[] = { {"⌾ Teğet yüzleri seç (fillet zinciri)", "⌾", AkilliSecim::TanjantYuz}, {"▱ Koplanar yüzleri seç", "▱", AkilliSecim::KoplanarYuz}, {"∥ Paralel yüzleri seç", "∥", AkilliSecim::ParalelYuz}, {"⊘ Aynı ölçüdeki yüzleri seç (bu çaptaki delikler)", "⊘", AkilliSecim::BenzerYuz}, {"◠ Teğet kenar zincirini seç (loop)", "◠", AkilliSecim::KenarZinciri}, {"◯ Aynı ölçüdeki kenarları seç (bu R'deki filletler)", "◯", AkilliSecim::BenzerKenar}, {"🎨 Aynı renkli yüzleri seç", "🎨", AkilliSecim::AyniRenkYuz}, }; for (const AsKayit& k : kAs) { const AkilliSecim tur = k.tur; // bilgiVer = bglm.bilgiVer (UygulamaBaglam üyesi, uzun ömürlü) → referansla // yakalamak güvenli; çevredeki komutlar da aynı kalıbı kullanıyor. komut("Seçim", k.ad, k.simge, [&sahne, &bilgiVer, tur](Cekirdek&) { const int n = akilliSecimUygula(sahne, tur); bilgiVer(n > 0 ? (std::to_string(n) + " " + akilliSecimAd(tur) + " seçime eklendi") : std::string("Uygun seçim yok — önce doğru kipte bir ") + (tur == AkilliSecim::KenarZinciri || tur == AkilliSecim::BenzerKenar ? "kenar" : "yüz") + " seç"); }); } } komut("Araçlar", "🔧 FikoYT Diagonal Kafa", "🔧", [&](Cekirdek&) { kafaAc(KafaTip::Diagonal); }); komut("Araçlar", "🔩 FikoYT Ozmo Kafa", "🔩", [&](Cekirdek&) { kafaAc(KafaTip::Ozmo); }); komut("Araçlar", "🔧 FikoYT Diagonal Kafa (Ayrı Pencerede)", "🔧", [&](Cekirdek&) { kafaAyriPencerdeAc(KafaTip::Diagonal, pencere_.dogalTutamac()); }); komut("Araçlar", "🔩 FikoYT Ozmo Kafa (Ayrı Pencerede)", "🔩", [&](Cekirdek&) { kafaAyriPencerdeAc(KafaTip::Ozmo, pencere_.dogalTutamac()); }); // ---- Part Design feature'ları (HER FEATURE KENDİ PENCERESİNDE) ---- // Hedef katı: önce seçili geçerli brep gövde, yoksa sahnedeki son geçerli katı (CATIA // InWork gibi). Çıkarma/kenar feature'ları hedef seçili olmasa da Pad gibi dialog açar. const auto hedefKatiBul = [&]() -> u32 { try { // brep erişimi nadiren OSD_ThreadPool/mesh ile çakışıp deadlock atabilir → 0 dön for (u32 g : sahne.seciliGovdeler()) if (Dugum* d = sahne.bul(g); d && d->brep && d->brep->gecerliMi()) return g; u32 son = 0; for (auto& d : sahne.dugumler()) if (d.brep && d.brep->gecerliMi()) son = d.kimlik; return son; } catch (...) { return 0; // hedef bulunamadı → feature yine de pencere açar (hata satırıyla) } }; // Pad: aktif (dolu) sketch profilinden prizma katı. Pocket: aktif sketch + // seçili hedef katıdan boolean çıkarma. Taslak hemen sahnede önizlenir. komut("Feature", "Pad (Kabartma)", "⬒", [&](Cekirdek&) { const u32 k = partD.featureBaslat("pad", aktifSketch); // aktif yoksa seçili sketch'e düşer if (k) featurePencereAc(k); else bilgiVer("Pad için bir sketch açın veya seçin (profil kapalı olabilir)"); }, "part", etkinSketch, "olustur"); komut("Feature", "Pocket (Cep)", "⬓", [&](Cekirdek&) { const u32 k = partD.featureBaslat("pocket", aktifSketch, hedefKatiBul()); if (k) featurePencereAc(k); // pencere her zaman açılır; eksik girdi pencere içinde tamamlanır }, "part", etkinSketch, "olustur"); // Shaft (Döndür): aktif sketch profilini eksen etrafında döndürerek katı ekle. komut("Feature", "Shaft (Döndür)", "⟳", [&](Cekirdek&) { const u32 k = partD.featureBaslat("shaft", aktifSketch); // aktif yoksa seçili sketch'e düşer if (k) featurePencereAc(k); else bilgiVer("Shaft için bir sketch açın veya seçin (profil kapalı olabilir)"); }, "part", etkinSketch, "olustur"); // Groove (Oyuk): aktif sketch + hedef katıdan döndürerek boolean çıkarma. komut("Feature", "Groove (Oyuk)", "◠", [&](Cekirdek&) { const u32 k = partD.featureBaslat("groove", aktifSketch, hedefKatiBul()); if (k) featurePencereAc(k); else bilgiVer("Groove için kapalı bir sketch açın/seçin ve bir hedef katı bulunsun"); }, "part", etkinSketch, "olustur"); // Rib (Kaburga): aktif sketch profilini bir kılavuz eğri (center curve) boyunca süpürerek // katı EKLE. Profil = aktif/seçili sketch; kılavuz pencerede ayrıca seçilir (2. sketch). komut("Feature", "Rib (Kaburga)", "◤", [&](Cekirdek&) { const u32 k = partD.featureBaslat("rib", aktifSketch); // aktif yoksa seçili sketch'e düşer if (k) featurePencereAc(k); else bilgiVer("Rib için kapalı bir profil sketch'i açın veya seçin"); }, "part", etkinSketch, "olustur"); // Slot (Kanal): Rib ile aynı süpürme ama hedef katıdan boolean ÇIKAR. komut("Feature", "Slot (Kanal)", "◥", [&](Cekirdek&) { const u32 k = partD.featureBaslat("slot", aktifSketch, hedefKatiBul()); if (k) featurePencereAc(k); else bilgiVer("Slot için kapalı profil sketch'i ve bir hedef katı gerekli"); }, "part", etkinSketch, "olustur"); // Stiffener (Nervür): AÇIK profil kesitini kalınlaştırıp prizma → hedef katı ile Fuse. // Profil = aktif/seçili sketch (açık çizgi); hedef katı varsa birleşir, yoksa tek nervür. komut("Feature", "Stiffener (Nervür)", "◣", [&](Cekirdek&) { const u32 k = partD.featureBaslat("stiffener", aktifSketch, hedefKatiBul()); if (k) featurePencereAc(k); else bilgiVer("Nervür için açık bir profil sketch'i açın veya seçin"); }, "part", etkinSketch, "olustur"); // Loft (Çok-Kesitli Katı): 1. kesit = aktif/seçili sketch; ek kesitler pencerede "➕ Kesit // Ekle" ile seçilir → kesitlerden geçen katı EKLE (ThruSections). En az 2 kesit gerekir. komut("Feature", "Loft (Çok-Kesitli)", "⧉", [&](Cekirdek&) { const u32 k = partD.featureBaslat("loft", aktifSketch); // aktif yoksa seçili sketch'e düşer if (k) featurePencereAc(k); else bilgiVer("Loft için kapalı bir profil sketch'i açın veya seçin (ek kesitler pencerede)"); }, "part", etkinSketch, "olustur"); // Removed Loft (Çok-Kesitli Cep): Loft ile aynı ama hedef katıdan boolean ÇIKAR. komut("Feature", "Removed Loft (Çıkar)", "⧅", [&](Cekirdek&) { const u32 k = partD.featureBaslat("loft_cikar", aktifSketch, hedefKatiBul()); if (k) featurePencereAc(k); else bilgiVer("Removed Loft için kapalı profil sketch'i ve bir hedef katı gerekli"); }, "part", etkinSketch, "olustur"); // Solid Combine (Kesişim Katı / CATIA Combine): 1. profil = aktif/seçili sketch; 2. profil // pencerede seçilir → her profil kendi normalinde prizmalanıp kesişir (öteleme kesişimi). komut("Feature", "Solid Combine (Kesişim)", "⧓", [&](Cekirdek&) { const u32 k = partD.featureBaslat("solid_combine", aktifSketch); // aktif yoksa seçili sketch'e düşer if (k) featurePencereAc(k); else bilgiVer("Solid Combine için kapalı bir profil sketch'i açın/seçin (2. profil pencerede)"); }, "part", etkinSketch, "olustur"); // Hole (Delik): sketch GEREKMEZ; sayısal konum + seçili hedef katıdan çıkarma. komut("Feature", "Hole (Delik)", "◎", [&](Cekirdek&) { const u32 k = partD.featureBaslat("hole", VARLIK_YOK, hedefKatiBul()); if (k) featurePencereAc(k); else bilgiVer("Hole için bir hedef katı gövde bulunsun"); }, "part", etkinBrepli, "olustur"); // Fillet/Chamfer (kenar) + Shell/Draft (yüz): sketch GEREKMEZ. Seçili katı gövde = // hedef; seçili alt-öğeler (kenar/yüz) ön-doldurulur, pencerede 🎯 ile eklenir/kaldırılır. // altPrefix="kenar"/"yuz", sayiKey="kenarSayi"/"yuzSayi" → parametrik feature. const auto cokluAltBaslat = [&](const char* tip, const char* etiketAd, const char* altPrefix, const char* sayiKey) { const u32 hedef = hedefKatiBul(); const u32 k = partD.featureBaslat(tip, VARLIK_YOK, hedef); if (!k) { bilgiVer(std::string(etiketAd) + " için bir katı gövde bulunsun"); return; } // Hedefe ait seçili alt-öğeleri ön-doldur (kenar Kenar-kipinde, yüz Yüz-kipinde). // TOPLU + tek recompute (öğe başına featureGuncelle ayrı tam recompute koşturuyordu). std::vector> params; int n = 0; for (const auto& o : sahne.secililer()) if (o.alt > 0 && o.govde == hedef) params.emplace_back(altPrefix + std::to_string(n++), (f64)o.alt); if (n) { params.emplace_back(sayiKey, (f64)n); partD.featureParamToplu(k, params); } featurePencereAc(k); }; komut("Feature", "Fillet (Kenar Yuvarlat)", "◜", [&, cokluAltBaslat](Cekirdek&) { cokluAltBaslat("fillet", "Fillet", "kenar", "kenarSayi"); }, "part", etkinBrepli, "cila"); komut("Feature", "Chamfer (Pah)", "◺", [&, cokluAltBaslat](Cekirdek&) { cokluAltBaslat("chamfer", "Chamfer", "kenar", "kenarSayi"); }, "part", etkinBrepli, "cila"); // Fillet varyantları: Variable (tek kenar) + Face-Face / Tritangent (2-3 yüz). komut("Feature", "Variable Fillet (Değişken)", "◝", [&, cokluAltBaslat](Cekirdek&) { cokluAltBaslat("fillet_var", "Variable Fillet", "kenar", "kenarSayi"); }, "part", etkinBrepli, "cila"); komut("Feature", "Face-Face Fillet (Yüz-Yüz)", "◞", [&, cokluAltBaslat](Cekirdek&) { cokluAltBaslat("fillet_ff", "Face-Face Fillet", "yuz", "yuzSayi"); }, "part", etkinBrepli, "cila"); komut("Feature", "Tritangent Fillet (Üç-Teğet)", "◟", [&, cokluAltBaslat](Cekirdek&) { cokluAltBaslat("fillet_tri", "Tritangent Fillet", "yuz", "yuzSayi"); }, "part", etkinBrepli, "cila"); // Shell/Draft: yüz seçimli (Yüz kipinde ön-seçili yüzler ordinal olarak dolar; pencere // 🎯 Yüz Ekle beklenenTur=1 ile pick filtresini Yüze kilitler). komut("Feature", "Shell (İçi Boşalt)", "◳", [&, cokluAltBaslat](Cekirdek&) { cokluAltBaslat("shell", "Shell", "yuz", "yuzSayi"); }, "part", etkinBrepli, "cila"); komut("Feature", "Draft (Yüz Açısı)", "◿", [&, cokluAltBaslat](Cekirdek&) { cokluAltBaslat("draft", "Draft", "yuz", "yuzSayi"); }, "part", etkinBrepli, "cila"); // Draft Reflect Line (Yansıma Hattı): Draft ile aynı yüz seçimi; nötr eleman otomatik (yansıma hattı). komut("Feature", "Draft Reflect Line (Yansıma)", "◍", [&, cokluAltBaslat](Cekirdek&) { cokluAltBaslat("draft_reflect", "Draft Reflect Line", "yuz", "yuzSayi"); }, "part", etkinBrepli, "cila"); // Remove Face (Yüz Kaldır — CATIA Part Design parametrik): seçili yüzleri sil + komşu uzat → // ağaçta düğüm, çift-tık düzenleme, suppress/rollback. DM "Değiştir > Yüz Kaldır" doğrudan yolunun ikizi. komut("Feature", "Remove Face (Yüz Kaldır)", "⊘", [&, cokluAltBaslat](Cekirdek&) { cokluAltBaslat("remove_face", "Remove Face", "yuz", "yuzSayi"); }, "part", etkinBrepli, "cila"); // ---- Sew Surface / Replace Face (Yüzey grubu): hedef katı + kaynak yüzey (2 gövde) ---- // Hacme göre ayır: kapalı katı (hacim>0) = hedef; açık yüzey (hacim~0) = kaynak. Kaynak // yüzey kaynakDugum param'ına yazılır; Replace Face için hedefe ait seçili yüz yuz0'a ön-doldurulur. const auto yuzeyKaynakBaslat = [&](const char* tip, const char* etiketAd) { u32 kati = 0, yuzey = 0; for (u32 g : sahne.seciliGovdeler()) { Dugum* d = sahne.bul(g); if (!d || !d->brep || !d->brep->gecerliMi()) continue; if (d->brep->hacim() > 1e-6) { if (!kati) kati = g; } else if (!yuzey) yuzey = g; } if (!kati) kati = hedefKatiBul(); if (!kati || !yuzey) { bilgiVer(std::string(etiketAd) + " için 1 katı + 1 açık yüzey seçin (Ctrl+tık)"); return; } const u32 k = partD.featureBaslat(tip, VARLIK_YOK, kati); if (!k) { bilgiVer(std::string(etiketAd) + " başlatılamadı (hedef katı gerekli)"); return; } // Replace Face: hedefe ait seçili yüzü (varsa) eski yüz olarak ön-doldur (side yönü için). // TOPLU + tek recompute. std::vector> params{{"kaynakDugum", (f64)yuzey}}; int n = 0; for (const auto& o : sahne.secililer()) if (o.alt > 0 && o.govde == kati) params.emplace_back("yuz" + std::to_string(n++), (f64)o.alt); if (n) params.emplace_back("yuzSayi", (f64)n); partD.featureParamToplu(k, params); featurePencereAc(k); }; komut("Yüzey", "Sew Surface (Yüzey Dik)", "◤", [&, yuzeyKaynakBaslat](Cekirdek&) { yuzeyKaynakBaslat("sew_surface", "Sew Surface"); }, "part", etkinBrepli, "yuzey"); komut("Yüzey", "Replace Face (Yüz Değiştir)", "▨", [&, yuzeyKaynakBaslat](Cekirdek&) { yuzeyKaynakBaslat("replace_face", "Replace Face"); }, "part", etkinBrepli, "yuzey"); // Pattern (Dik/Dairesel Dizi) + Mirror (Ayna): sketch/kenar/yüz seçimi GEREKMEZ. // Seçili katı gövde = hedef; sonuç kopyaların birleşimidir (Pad gibi düz param penceresi). const auto patternBaslat = [&](const char* tip, const char* etiketAd) { const u32 k = partD.featureBaslat(tip, VARLIK_YOK, hedefKatiBul()); if (k) featurePencereAc(k); else bilgiVer(std::string(etiketAd) + " için bir katı gövde bulunsun"); }; komut("Feature", "Dik Dizi", "▦", [&, patternBaslat](Cekirdek&) { patternBaslat("pattern_dik", "Dik Dizi"); }, "part", etkinBrepli, "cogalt"); komut("Feature", "Dairesel Dizi", "◍", [&, patternBaslat](Cekirdek&) { patternBaslat("pattern_dairesel", "Dairesel Dizi"); }, "part", etkinBrepli, "cogalt"); // Kullanıcı Dizi (User Pattern): hedef katıyı bir konum sketch'inin NOKTA konumlarına kopyalar. // patternBaslat kalıbı (hedef katı + düz param penceresi); konum sketch'i pencerede seçilir. komut("Feature", "Kullanıcı Dizi", "✸", [&, patternBaslat](Cekirdek&) { patternBaslat("pattern_user", "Kullanıcı Dizi"); }, "part", etkinBrepli, "cogalt"); komut("Feature", "Ayna (Mirror)", "◪", [&, patternBaslat](Cekirdek&) { patternBaslat("mirror", "Ayna"); }, "part", etkinBrepli, "cogalt"); // Transformation (Öteleme/Döndürme/Simetri/Ölçek): sketch/seçim GEREKMEZ; seçili katı gövde = // hedef; sonuç dönüştürülmüş katıdır (orijinal gizlenir). patternBaslat kalıbı (hedef katı + // düz param penceresi). "donustur" flyout kümesi (CATIA Transformation Features grubu). komut("Feature", "Translation (Öteleme)", "➜", [&, patternBaslat](Cekirdek&) { patternBaslat("translation", "Öteleme"); }, "part", etkinBrepli, "donustur"); komut("Feature", "Rotation (Döndürme)", "⟲", [&, patternBaslat](Cekirdek&) { patternBaslat("rotation", "Döndürme"); }, "part", etkinBrepli, "donustur"); komut("Feature", "Symmetry (Simetri)", "◨", [&, patternBaslat](Cekirdek&) { patternBaslat("symmetry", "Simetri"); }, "part", etkinBrepli, "donustur"); komut("Feature", "Scaling (Ölçekleme)", "⤢", [&, patternBaslat](Cekirdek&) { patternBaslat("scaling", "Ölçekleme"); }, "part", etkinBrepli, "donustur"); // Thickness (Kalınlık): sketch/seçim GEREKMEZ; seçili katıyı uniform offset'ler (±). // patternBaslat kalıbı (hedef katı + düz param penceresi). "cila" flyout'ta (Shell yanı). komut("Feature", "Thickness (Kalınlık)", "◲", [&, patternBaslat](Cekirdek&) { patternBaslat("thickness", "Thickness"); }, "part", etkinBrepli, "cila"); // Boolean Bodies (Add/Remove/Intersect): 2 SEÇİLİ katı gövde arası parametrik boolean // (ürün ağacında feature; primitif "Boolean" grubundakiler anlık/undo, bunlar DAG). İlk // geçerli brep gövde = A (hedef), ikinci = B (ikinciGovde param). featureGuncelle B'yi // yazar → recompute; sonuç tek gövde, A/B gizlenir. const auto boolGovdeBaslat = [&](const char* tip, const char* etiketAd) { u32 a = 0, b = 0; for (u32 g : sahne.seciliGovdeler()) { if (Dugum* d = sahne.bul(g); d && d->brep && d->brep->gecerliMi()) { if (!a) a = g; else { b = g; break; } } } if (!a || !b) { bilgiVer(std::string(etiketAd) + " için 2 katı gövde seçin (Ctrl+tık)"); return; } const u32 k = partD.featureBaslat(tip, VARLIK_YOK, a); if (!k) { bilgiVer(std::string(etiketAd) + " başlatılamadı"); return; } partD.featureGuncelle(k, "ikinciGovde", (f64)b); // 2. gövde → yeniden hesapla featurePencereAc(k); }; komut("Boolean", "Add (Gövde Birleştir)", "⊕", [&, boolGovdeBaslat](Cekirdek&) { boolGovdeBaslat("bool_add", "Add"); }, "part", etkinIkiGovde, "govde"); komut("Boolean", "Remove (Gövde Çıkar)", "⊖", [&, boolGovdeBaslat](Cekirdek&) { boolGovdeBaslat("bool_remove", "Remove"); }, "part", etkinIkiGovde, "govde"); komut("Boolean", "Intersect (Gövde Kesişim)", "⊗", [&, boolGovdeBaslat](Cekirdek&) { boolGovdeBaslat("bool_intersect", "Intersect"); }, "part", etkinIkiGovde, "govde"); // Union Trim (Birleşim-Kırp): 2 gövde Fuse + sonuç parçalarından seçili yüzü at (boolGovdeBaslat // kalıbı — A/B iki seçili gövde; kaldırılacak yüz pencerede fused sonuç üzerinde seçilir). komut("Boolean", "Union Trim (Birleşim-Kırp)", "⊍", [&, boolGovdeBaslat](Cekirdek&) { boolGovdeBaslat("union_trim", "Union Trim"); }, "part", etkinIkiGovde, "govde"); // Remove Lump (Parça Ayıkla): tek gövde birden çok ayrık katı parçaya bölünmüşse, seçili yüzün // ait olduğu parçayı tut/at (tek hedef katı; parça yüzü pencerede seçilir). komut("Boolean", "Remove Lump (Parça Ayıkla)", "⊟", [&, patternBaslat](Cekirdek&) { patternBaslat("remove_lump", "Remove Lump"); }, "part", etkinBrepli, "govde"); // ---- Referans Öğeler (CATIA Reference Elements): datum Nokta/Çizgi/Düzlem ---- // Katı DEĞİL — feature'lara referans (Sketch düzlemi, Pad yönü, Mirror düzlemi). // Her komut ortak "Referans Öğe" penceresini ilgili sekmeyle açar. komut("Referans", "Nokta", "•", [](Cekirdek&) { referansPencereAc(0); }, "part", nullptr, "referans"); komut("Referans", "Çizgi", "╱", [](Cekirdek&) { referansPencereAc(1); }, "part", nullptr, "referans"); komut("Referans", "Düzlem", "◱", [](Cekirdek&) { referansPencereAc(2); }, "part", nullptr, "referans"); // ---- Analiz WB komutları (her analiz KENDİ penceresinde) ---- const auto analizHedefSec = [&]() -> u32 { for (u32 g : sahne.seciliGovdeler()) if (Dugum* d = sahne.bul(g); d && d->brep && d->brep->gecerliMi()) return g; return 0; // 0 = ilk görünür gövde (AnalizSistemi çözer) }; // analizHedefSec kur()-yerel → DEĞERLE yakala (yalnız sahne heap-ref taşır → güvenli; // [&] dangling = 11 Analiz/Döküm komutu kareCiz'de 0xC0000005). analizEkleKomut de yerel // → tüketici lambda'lar [&, analizEkleKomut] ile onu DEĞERLE kopyalar (zincir kararlı). const auto analizEkleKomut = [&, analizHedefSec](analiz::AnalizTip tip) { const u32 k = analizSis.ekle(tip, analizHedefSec()); if (k) { analizPencereAc(k); analizSis.acik = true; } }; komut("Analiz", "Çekme Açısı", "◿", [&, analizEkleKomut](Cekirdek&) { analizEkleKomut(analiz::AnalizTip::Draft); }, "analiz", etkinBrepli); komut("Analiz", "Undercut", "⛏", [&, analizEkleKomut](Cekirdek&) { analizEkleKomut(analiz::AnalizTip::Undercut); }, "analiz", etkinBrepli); komut("Analiz", "Et Kalınlığı", "▤", [&, analizEkleKomut](Cekirdek&) { analizEkleKomut(analiz::AnalizTip::Kalinlik); }, "analiz", etkinBrepli); komut("Analiz", "Radius", "◜", [&, analizEkleKomut](Cekirdek&) { analizEkleKomut(analiz::AnalizTip::Radius); }, "analiz", etkinBrepli); komut("Analiz", "Kurvatür", "◠", [&, analizEkleKomut](Cekirdek&) { analizEkleKomut(analiz::AnalizTip::Kurvatur); }, "analiz", etkinBrepli); komut("Analiz", "Süreklilik", "≈", [&, analizEkleKomut](Cekirdek&) { analizEkleKomut(analiz::AnalizTip::Sureklilik); }, "analiz", etkinBrepli); komut("Analiz", "Keskin Kenar", "◣", [&, analizEkleKomut](Cekirdek&) { analizEkleKomut(analiz::AnalizTip::KeskinKenar); }, "analiz", etkinBrepli); // Zekâ Analizi: Geometri Zekâ Motoru — feature tanıma (delik/cep/fillet/pah/pattern/ // simetri). zeka.hesap_async açıkken ağır OCCT okumaları worker'da (zekaCoz — // zeka_penceresi.cpp ortak akış); özet hesap bitince verilir. komut("Analiz", "Zekâ Analizi", "🧠", [&, analizHedefSec](Cekirdek&) { if (!zekaAktif()) { bilgiVer("Zekâ Motoru kapalı — Ayarlar → Zekâ bölümünden açın"); return; } const u32 hedef = analizHedefSec(); Dugum* dz = hedef ? sahne.bul(hedef) : nullptr; if (!dz || !dz->brep || !dz->brep->gecerliMi()) { bilgiVer("Zekâ Analizi için geçerli bir BREP gövdesi seçin"); return; } zekaPencereAc(hedef); // asıl arayüz: gruplu feature raporu (satır tıkla → yüz seç+odak) const auto ozetVer = [&, hedef] { struct TipEtiket { const char* tip; const char* etiket; }; static const TipEtiket tipler[] = { {"Delik", "delik"}, {"Cep", "cep"}, {"Slot", "slot"}, {"Boss", "boss"}, {"FilletZinciri", "fillet zinciri"}, {"Fillet", "fillet"}, {"Pah", "pah"}, {"Pattern", "pattern"}, {"SimetriDuzlemi", "simetri düzlemi"}}; // Feature düğümlerinin BREP bağı yok — govdede() süzgeci onları ELER (yaşandı: // özet hep "bulunamadı" çıkıyordu). Sayım motorun feature API'sinden. std::unordered_map sayim; for (u64 fk : belge.zekaSis.govdeFeatureleri(hedef)) if (const zeka::ZDugum* fd = belge.zekaSis.graf.dugum(fk)) ++sayim[belge.zekaSis.graf.adMetin(fd->tip)]; std::string ozet; for (const TipEtiket& t : tipler) { const auto si = sayim.find(t.tip); if (si == sayim.end() || !si->second) continue; if (!ozet.empty()) ozet += ", "; ozet += std::to_string(si->second) + " " + t.etiket; } bilgiVer(ozet.empty() ? "Zekâ Analizi: tanınan feature bulunamadı" : "Zekâ Analizi: " + ozet + " bulundu"); // DFM rapor özeti auto dfmRaporlar = belge.zekaSis.graf.tipteki("DfmRapor"); for (u64 r : dfmRaporlar) { const i64 toplam = static_cast(belge.zekaSis.graf.ozSayi(r, "toplam_sorun")); const f64 skor = belge.zekaSis.graf.ozSayi(r, "risk_skoru"); if (toplam > 0) { char dfmBuf[128]; std::snprintf(dfmBuf, sizeof(dfmBuf), "DFM: %lld sorun, risk skoru %.0f/100", (long long)toplam, skor); bilgiVer(dfmBuf); } } }; if (zekaCoz(cekirdek, sahne, belge.zekaSis, hedef, ozetVer)) bilgiVer("⏳ Zekâ analizi arka planda sürüyor…"); }, "analiz", etkinBrepli); komut("Analiz", "Zebra", "▦", [&, analizEkleKomut](Cekirdek&) { analizEkleKomut(analiz::AnalizTip::Zebra); }, "analiz", etkinBrepli); komut("Analiz", "Yansıma Hattı", "◍", [&, analizEkleKomut](Cekirdek&) { analizEkleKomut(analiz::AnalizTip::Reflect); }, "analiz", etkinBrepli); // CAD Fark: TEK panel — mevcut Fark analizi varsa onu aç, yoksa oluştur (web // CadDiff paneli gibi tekil). "Web Fark" adıyla Doğrudan Modelleme'de de aranır. const auto farkPanelKomut = [&, analizHedefSec](Cekirdek&) { u32 k = 0; for (u32 kk : analizSis.hepsi()) if (analiz::AnalizNesnesi* fa = analizSis.bul(kk); fa && fa->tip() == analiz::AnalizTip::Fark) { k = kk; break; } if (!k) k = analizSis.ekle(analiz::AnalizTip::Fark, analizHedefSec()); if (k) { analizPencereAc(k); analizSis.acik = true; } }; komut("Analiz", "CAD Fark", "⇄", farkPanelKomut, "analiz", etkinBrepli); komut("Doğrudan", "Web Fark (CAD Karşılaştırma)", "⇄", farkPanelKomut, "dogrudan", etkinBrepli); komut("Analiz", "Kesit Düzlemi", "✂", [&, analizHedefSec](Cekirdek&) { const u32 k = analizSis.kesitEkle(analizHedefSec()); if (k) { kesitPencereAc(k); analizSis.acik = true; } }, "analiz", etkinBrepli); komut("Analiz", "Kesit Listesi", "≣", [&](Cekirdek&) { kesitListePencereAc(); analizSis.acik = true; }, "analiz"); // ---- Döküm Sim WB komutları ---- komut("Döküm", "Döküm Sim Oluştur", "🔥", [&, analizHedefSec](Cekirdek&) { const u32 k = dokumSis.ekle(analizHedefSec()); if (k) { dokumPencereAc(k); dokumSis.acik = true; } }, "dokum", etkinBrepli); komut("Döküm", "Döküm Sim Çalıştır", "▶", [&, analizHedefSec](Cekirdek&) { // Açık/son eklenen simülasyonu çöz; yoksa yeni oluştur + çöz. u32 k = dokumSis.hepsi().empty() ? dokumSis.ekle(analizHedefSec()) : dokumSis.hepsi().back(); if (k) { dokumPencereAc(k); dokumSis.acik = true; dokumSis.cozumBaslat(k); } }, "dokum", etkinBrepli); // ---- Strafor Kesici (EPS montaj planlayıcı) ---- komut("Döküm", "Strafor Kesici", "🧊", [&](Cekirdek&) { straforPencereAc(); }, "dokum"); // 3B Pah / Yuvarlatma (CATIA Chamfer/Fillet): kenar seç → değer gir → uygula. komut("İşlem", "Pah (3B)", "◺", [&](Cekirdek&) { if (sahne.kip() != SecimKip::Kenar) sahne.kipAyarla(SecimKip::Kenar); pah3DAcik = true; pah3DYuvarlat = false; bilgiVer("Kenar(lar) seçin → mesafe girip Uygula"); }, "part"); komut("İşlem", "Kenar Yuvarlat (3B)", "◜", [&](Cekirdek&) { if (sahne.kip() != SecimKip::Kenar) sahne.kipAyarla(SecimKip::Kenar); pah3DAcik = true; pah3DYuvarlat = true; bilgiVer("Kenar(lar) seçin → yarıçap girip Uygula"); }, "part"); // Kenarı Taşı (Plasticity move-edge MVP): seçili KATI kenarını komşu yüz normallerinin // açıortayı boyunca tasiMesafe kadar taşı (± ayardan). Komşu düzlemsel yüzler eğilir. komut("İşlem", "Kenarı Taşı", "⇱", [&](Cekirdek&) { u32 gv = 0, ord = 0; for (const SecimOge& s : sahne.secililer()) if (s.alt != 0) { gv = s.govde; ord = s.alt; break; } if (!gv || sahne.kip() != SecimKip::Kenar) { if (sahne.kip() != SecimKip::Kenar) sahne.kipAyarla(SecimKip::Kenar); bilgiVer("Kenar kipinde bir katı kenarı seçip tekrar çalıştırın"); return; } Dugum* d = sahne.bul(gv); if (!d || !d->brep || !d->brep->gecerliMi()) return; Vec3d yon{}; // yön = komşu yüz normallerinin ortalaması (kenarın dışa açıortayı) for (u32 f : d->brep->kenarKomsuYuzler(ord)) { geometri::YuzBilgi yb; if (d->brep->yuzBilgi(f, yb, false)) yon = yon + yb.normal; } if (yon.uzunluk() < 1e-9) { bilgiVer("Kenar taşıma yönü çözülemedi"); return; } yon = yon.birim(); if (sahne.kenarTasiUygula(gv, ord, yon * (f64)dogrudanAyar().tasiMesafe)) ui::toastGoster("Kenar taşındı", ui::ToastTip::Basari); else ui::toastGoster("Kenar taşınamadı (düz kenar + düzlemsel komşu yüzler gerekli)", ui::ToastTip::Hata); }, "part", etkinSecimVar); // ---- GSD: HER FEATURE KENDİ PENCERESİNDE (gerçek OCCT B-Rep) ---- // Wireframe (bağımsız): Nokta / Çizgi / Çember / Spline → TopoDS_Edge/Vertex. const auto gsdEkle = [&](const char* tip) { const u32 k = gsdSis.gsdBaslat(tip, VARLIK_YOK); if (k) gsdPencereAc(k); else bilgiVer("GSD öğesi başlatılamadı"); }; komut("Tel-Kafes", "Nokta", "•", [&, gsdEkle](Cekirdek&) { gsdEkle("nokta"); }, "gsd"); komut("Tel-Kafes", "Çizgi", "╱", [&, gsdEkle](Cekirdek&) { gsdEkle("cizgi"); }, "gsd"); komut("Tel-Kafes", "Çember", "◯", [&, gsdEkle](Cekirdek&) { gsdEkle("cember"); }, "gsd"); komut("Tel-Kafes", "Spline", "∿", [&, gsdEkle](Cekirdek&) { gsdEkle("spline"); }, "gsd"); komut("Tel-Kafes", "Polyline", "⋀", [&, gsdEkle](Cekirdek&) { gsdEkle("polyline"); }, "gsd"); komut("Tel-Kafes", "Helis", "➰", [&, gsdEkle](Cekirdek&) { gsdEkle("helix"); }, "gsd"); komut("Tel-Kafes", "Konik", "◡", [&, gsdEkle](Cekirdek&) { gsdEkle("konik"); }, "gsd"); komut("Tel-Kafes", "Köşe", "⌒", [&, gsdEkle](Cekirdek&) { gsdEkle("kose"); }, "gsd"); // Yüzey (aktif sketch profili gerekli): Extrude / Revolve → açık shell. const auto gsdYuzeyEkle = [&](const char* tip) { const u32 k = gsdSis.gsdBaslat(tip, aktifSketch); // aktif yoksa seçili sketch'e düşer if (k) gsdPencereAc(k); else bilgiVer("Yüzey için bir sketch açın veya seçin (profil geçerli mi?)"); }; komut("Yüzey", "Yüzey Extrude", "▱", [&, gsdYuzeyEkle](Cekirdek&) { gsdYuzeyEkle("yuzeyExtrude"); }, "gsd", etkinSketch); komut("Yüzey", "Yüzey Revolve", "◗", [&, gsdYuzeyEkle](Cekirdek&) { gsdYuzeyEkle("yuzeyRevolve"); }, "gsd", etkinSketch); komut("Yüzey", "Yüzey Fill", "▰", [&, gsdYuzeyEkle](Cekirdek&) { gsdYuzeyEkle("yuzeyFill"); }, "gsd", etkinSketch); // Yüzey-üzeri op: seçili GSD yüzey düğümüne uygulanır (kaynak seçili yüzeye düşer). const auto gsdYuzeyOpEkle = [&](const char* tip) { // Seçili düğüm AYNI-TİP feature'ın SONUCU ise yeni feature yığma → mevcut feature'ın // penceresini aç (re-edit; gsd_tek_feature ayarı). Değilse eski yol: yeni taslak. Varlik sel = sahne.seciliGsdYuzey(); if (sel == VARLIK_YOK) sel = sahne.seciliGsdTel(); if (const u32 ef = gsdAyniTipFeature(gsdSis, sel, tip)) { gsdPencereAc(ef); return; } const u32 k = gsdSis.gsdBaslat(tip, VARLIK_YOK, VARLIK_YOK); if (k) gsdPencereAc(k); else bilgiVer("Yüzey op için önce bir GSD yüzey seçin"); }; const auto etkinGsdYuzey = [&](Cekirdek&) { return sahne.seciliGsdYuzey() != VARLIK_YOK; }; komut("Yüzey", "Yüzey Offset", "⇉", [&, gsdYuzeyOpEkle](Cekirdek&) { gsdYuzeyOpEkle("yuzeyOffset"); }, "gsd", etkinGsdYuzey); komut("Yüzey", "Yüzey Uzat", "⤢", [&, gsdYuzeyOpEkle](Cekirdek&) { gsdYuzeyOpEkle("yuzeyExtrapolate"); }, "gsd", etkinGsdYuzey); komut("Yüzey", "Yüzey Kalınlaştır", "▤", [&, gsdYuzeyOpEkle](Cekirdek&) { gsdYuzeyOpEkle("yuzeyThicken"); }, "gsd", etkinGsdYuzey); komut("Yüzey", "Yüzey Sınırı", "◌", [&, gsdYuzeyOpEkle](Cekirdek&) { gsdYuzeyOpEkle("sinir"); }, "gsd", etkinGsdYuzey); komut("Yüzey", "Afin (Non-uniform)", "⤡", [&, gsdYuzeyOpEkle](Cekirdek&) { gsdYuzeyOpEkle("afin"); }, "gsd", etkinGsdYuzey); komut("Yüzey", "Onar (Heal)", "✚", [&, gsdYuzeyOpEkle](Cekirdek&) { gsdYuzeyOpEkle("onar"); }, "gsd", etkinGsdYuzey); komut("Yüzey", "Yüzey Şişir", "◒", [&, gsdYuzeyOpEkle](Cekirdek&) { gsdYuzeyOpEkle("yuzeySisir"); }, "gsd", etkinGsdYuzey); komut("Yüzey", "Yüzey Yumuşat", "≈", [&, gsdYuzeyOpEkle](Cekirdek&) { gsdYuzeyOpEkle("yuzeyDuzelt"); }, "gsd", etkinGsdYuzey); // Extract: seçili KATI yüzü bağımsız GSD yüzeye çıkar (hibrit modelleme köprüsü). const auto etkinYuzSecili = [&](Cekirdek&) { for (const SecimOge& s : sahne.secililer()) if (s.alt != 0) return true; return false; }; komut("Yüzey", "Yüzeye Çıkar", "⧉", [&](Cekirdek&) { Varlik gv = VARLIK_YOK; u32 ord = 0; for (const SecimOge& s : sahne.secililer()) if (s.alt != 0) { gv = s.govde; ord = s.alt; break; } if (gv == VARLIK_YOK) { bilgiVer("Bir katı yüzü seçin (yüz kipi)"); return; } const u32 k = gsdSis.gsdBaslat("yuzeyCikart", VARLIK_YOK, gv); if (!k) { bilgiVer("Yüzey çıkarılamadı"); return; } gsdSis.gsdGuncelle(k, "yuzOrd", (f64)ord); gsdPencereAc(k); }, "gsd", etkinYuzSecili); // Intersection: iki seçili gövdenin kesişim eğrisi (Tel). etkinIkiGovde: 1089'da tanımlı. komut("Yüzey", "Kesişim Eğrisi", "✕", [&](Cekirdek&) { std::vector gv; for (const SecimOge& s : sahne.secililer()) if (std::find(gv.begin(), gv.end(), s.govde) == gv.end()) gv.push_back(s.govde); if (gv.size() < 2) { bilgiVer("İki gövde seçin"); return; } const u32 k = gsdSis.gsdBaslat("kesisimEgri", VARLIK_YOK, gv[0], gv[1]); if (k) gsdPencereAc(k); else bilgiVer("Kesişim eğrisi oluşturulamadı"); }, "gsd", etkinIkiGovde); // Bridge: iki seçili öğe (kenar/tel/yüzey) arasına köprü yüzeyi. komut("Yüzey", "Köprüle", "◠", [&](Cekirdek&) { std::vector gv; for (const SecimOge& s : sahne.secililer()) if (std::find(gv.begin(), gv.end(), s.govde) == gv.end()) gv.push_back(s.govde); if (gv.size() < 2) { bilgiVer("İki kenar/tel seçin"); return; } const u32 k = gsdSis.gsdBaslat("koprule", VARLIK_YOK, gv[0], gv[1]); if (k) gsdPencereAc(k); else bilgiVer("Köprü kurulamadı"); }, "gsd", etkinIkiGovde); // Sweep: 1. seçili tel = profil, 2. seçili tel = kılavuz yol → açık süpürme yüzeyi. komut("Yüzey", "Süpürme Yüzey", "⇝", [&](Cekirdek&) { std::vector gv; for (const SecimOge& s : sahne.secililer()) if (std::find(gv.begin(), gv.end(), s.govde) == gv.end()) gv.push_back(s.govde); if (gv.size() < 2) { bilgiVer("İki tel seçin (profil + yol)"); return; } const u32 k = gsdSis.gsdBaslat("supurmeYuzey", VARLIK_YOK, gv[0], gv[1]); if (k) gsdPencereAc(k); else bilgiVer("Süpürme kurulamadı"); }, "gsd", etkinIkiGovde); // Loft (çok-kesitli yüzey): seçili teller SIRAYLA kesit → açık shell (2+ kesit; 3+ = N-kaynak). komut("Yüzey", "Loft Yüzey", "⧢", [&](Cekirdek&) { std::vector gv; for (const SecimOge& s : sahne.secililer()) if (std::find(gv.begin(), gv.end(), s.govde) == gv.end()) gv.push_back(s.govde); if (gv.size() < 2) { bilgiVer("En az iki kesit teli seçin (sırayla)"); return; } const u32 k = gsdSis.gsdBaslat("loftYuzey", VARLIK_YOK, gv[0], gv[1]); if (!k) { bilgiVer("Loft kurulamadı"); return; } if (gv.size() > 2) gsdSis.gsdEkKaynaklarAyarla(k, {gv.begin() + 2, gv.end()}); gsdPencereAc(k); }, "gsd", etkinIkiGovde); // Kılavuzlu Loft: kesitleri SIRAYLA seç, SON seçilen = kılavuz omurga. komut("Yüzey", "Kılavuzlu Loft", "⤳", [&](Cekirdek&) { std::vector gv; for (const SecimOge& s : sahne.secililer()) if (std::find(gv.begin(), gv.end(), s.govde) == gv.end()) gv.push_back(s.govde); if (gv.size() < 3) { bilgiVer("En az 2 kesit + 1 kılavuz seçin (kılavuz SON)"); return; } const u32 k = gsdSis.gsdBaslat("loftKilavuz", VARLIK_YOK, gv.back(), gv[0]); if (!k) { bilgiVer("Kılavuzlu loft kurulamadı"); return; } if (gv.size() > 3) gsdSis.gsdEkKaynaklarAyarla(k, {gv.begin() + 1, gv.end() - 1}); gsdPencereAc(k); }, "gsd", etkinIkiGovde); // Trim (yüzey kırp): 1. seçili = yüzey, 2. seçili = kesici (yüzey/eğri) → parçalara böl. komut("Yüzey", "Yüzey Kırp", "✂", [&](Cekirdek&) { std::vector gv; for (const SecimOge& s : sahne.secililer()) if (std::find(gv.begin(), gv.end(), s.govde) == gv.end()) gv.push_back(s.govde); if (gv.size() < 2) { bilgiVer("Yüzey + kesici seçin (2 öğe)"); return; } const u32 k = gsdSis.gsdBaslat("yuzeyKirp", VARLIK_YOK, gv[0], gv[1]); if (k) gsdPencereAc(k); else bilgiVer("Yüzey kırpma kurulamadı"); }, "gsd", etkinIkiGovde); // Fill/Patch (yama): KENAR kipinde seçili KATI kenarları (3+) → o kenarlardan yama; // değilse eski yol: seçili sınır telleri (kapalı tel ya da 3+ tel düğümü). komut("Yüzey", "Yama Yüzey", "▨", [&](Cekirdek&) { if (sahne.kip() == SecimKip::Kenar) { u32 kg = 0; // tek gövdenin seçili kenar ordinalleri std::vector ordlar; for (const SecimOge& s : sahne.secililer()) { if (s.alt == 0) continue; if (kg == 0) kg = s.govde; if (s.govde == kg) ordlar.push_back(s.alt); } if (kg && ordlar.size() >= 3) { const u32 k = gsdSis.gsdBaslat("yamaYuzey", VARLIK_YOK, kg); if (!k) { bilgiVer("Yama kurulamadı"); return; } std::vector> d{{"kenarSayi", (f64)ordlar.size()}}; for (size_t i = 0; i < ordlar.size(); ++i) d.push_back({"k" + std::to_string(i), (f64)ordlar[i]}); gsdSis.gsdGuncelleToplu(k, d); gsdPencereAc(k); return; } bilgiVer("Katı kenarından yama için aynı gövdede ≥3 kenar seçin"); return; } std::vector gv; for (const SecimOge& s : sahne.secililer()) if (std::find(gv.begin(), gv.end(), s.govde) == gv.end()) gv.push_back(s.govde); if (gv.empty()) { bilgiVer("Sınır teli/kenarları seçin"); return; } const u32 k = gsdSis.gsdBaslat("yamaYuzey", VARLIK_YOK, gv[0]); if (!k) { bilgiVer("Yama kurulamadı"); return; } if (gv.size() > 1) gsdSis.gsdEkKaynaklarAyarla(k, {gv.begin() + 1, gv.end()}); gsdPencereAc(k); }, "gsd", etkinSecimVar); // Concatenate: seçili GSD telin kenarlarını tek eğriye birleştir. const auto etkinGsdTel = [&](Cekirdek&) { return sahne.seciliGsdTel() != VARLIK_YOK; }; komut("Yüzey", "Eğri Birleştir", "⌇", [&, gsdYuzeyOpEkle](Cekirdek&) { gsdYuzeyOpEkle("birlestir"); }, "gsd", etkinGsdTel); komut("Görünüm", "💡 Işıklar", "💡", [&](Cekirdek&) { panelIsik = !panelIsik; }); komut("Görünüm", "🎨 Malzeme", "🎨", [](Cekirdek&) { malzemePencereAc(); }); // Makro/komut paleti köprüsü: pencere açmadan seçime uygula (fiko.komut.calistir). komut("Görünüm", "Malzeme Uygula", "🖌", [&](Cekirdek&) { if (!malzemeSeciliyeAta(*ort.bglmPtr, malzemeAktifKimlik())) bilgiVer("Malzeme uygulanamadı — sahnede gövde/yüz seçili değil"); }); komut("Görünüm", "Malzemeyi Kaldır", "🧽", [&](Cekirdek&) { if (!malzemeSeciliyeAta(*ort.bglmPtr, 0)) bilgiVer("Seçim yok — kaldırılacak atama bulunamadı"); }); komut("Görünüm", "Seçimi Çerçevele", "", [&](Cekirdek&) { secimiSigdir(); }); komut("Görünüm", "Tümünü Sığdır", "", [&](Cekirdek&) { tumunuSigdir(); }); komut("Görünüm", "İzometrik", "", [&](Cekirdek&) { kamera.gorunumeGit(HizliGorunum::Izometrik); }); komut("Görünüm", "Üst", "", [&](Cekirdek&) { kamera.gorunumeGit(HizliGorunum::Ust); }); komut("Görünüm", "Ön", "", [&](Cekirdek&) { kamera.gorunumeGit(HizliGorunum::On); }); komut("Görünüm", "Ortografik (O)", "", [&](Cekirdek&) { kamera.ortografikAyarla(!kamera.ortografik()); }); komut("Düzenle", "Seçileni Sil", "", [&](Cekirdek&) { seciliSil(); }, "", etkinSecimVar); // Gövdeyi Çoğalt: seçili gövde(ler)in kopyası (brep paylaşımlı instancing) → kopyalar seçilir. // Alt+sürükle kopyala-taşı da aynı temeli kullanır (yuz_tasi_gizmo Alt kancası). komut("Düzenle", "Gövdeyi Çoğalt", "⧉", [&](Cekirdek&) { std::vector yeniler; for (u32 g : sahne.seciliGovdeler()) if (const u32 y = sahne.govdeKopyalaUygula(g)) yeniler.push_back(y); if (yeniler.empty()) { bilgiVer("Çoğaltılacak (brep'li) gövde seçin"); return; } sahne.sec({yeniler.front(), 0}, false); for (size_t i = 1; i < yeniler.size(); ++i) sahne.sec({yeniler[i], 0}, true); ui::toastGoster("Gövde çoğaltıldı", ui::ToastTip::Basari); }, "", etkinSecimVar); komut("Düzenle", "Seçimi Temizle", "", [&](Cekirdek&) { sahne.secimTemizle(); }); // ---- Seçim kipi komutları: DOKUNMATİK İÇİN ZORUNLU ---- // Kip yalnız 1-4 KISAYOLLARIYLA değişiyordu → klavyesiz (parmak/kalem) makinede // yüz/kenar seçimine geçmek İMKÂNSIZDI. Registry'e alındı → bağlam menüsü + arama + // makro/plugin/olay erişimi (anayasa: her sistem bu üçünden erişilebilir). komut("Seçim", "◼ Gövde kipi (1)", "◼", [&](Cekirdek&) { sahne.kipAyarla(SecimKip::Govde); }); komut("Seçim", "▢ Yüz kipi (2)", "▢", [&](Cekirdek&) { sahne.kipAyarla(SecimKip::Yuz); }); komut("Seçim", "╱ Kenar kipi (3)", "╱", [&](Cekirdek&) { sahne.kipAyarla(SecimKip::Kenar); }); komut("Seçim", "• Nokta kipi (4)", "•", [&](Cekirdek&) { sahne.kipAyarla(SecimKip::Nokta); }); komut("Ölçüm", "Ölçüm Penceresi (M)", "↔", [&](Cekirdek&) { olcum.acik = !olcum.acik; if (olcum.acik && olcum.kip == OlcumKip::Ayarlar) olcum.kip = OlcumKip::Cetvel; }); komut("Ölçüm", "Kumpas (Tekli)", "", [&](Cekirdek&) { olcum.acik = true; olcum.kip = OlcumKip::Kumpas; olcum.secimSifirla(); }); komut("Ölçüm", "Cetvel (İkili)", "", [&](Cekirdek&) { olcum.acik = true; olcum.kip = OlcumKip::Cetvel; olcum.secimSifirla(); }); komut("Ölçüm", "Zincir (Ardışık)", "⛓", [&](Cekirdek&) { olcum.acik = true; olcum.kip = OlcumKip::Zincir; olcum.secimSifirla(); }); komut("Ölçüm", "Ölçümleri Temizle", "", [&](Cekirdek&) { olcum.temizle(); olcum.zincirTemizle(); }); komut("Ölçüm", "⚖ Ağırlık Hesaplama", "⚖", [&](Cekirdek&) { agirlikPencereAc(); }); komut("Part Design", "Push/Pull (Yüz İt/Çek)", "⇔", [&](Cekirdek&) { ort.pushPull.aktif = !ort.pushPull.aktif; ort.pushPull.surukluyor = false; }); komut("Görünüm", "✨ FikoUi Galerisi", "✨", [](Cekirdek&) { ui::galeriAcik() = !ui::galeriAcik(); }); komut("Görünüm", "Hover Bilgi Kartı", "", [&](Cekirdek&) { hoverBilgiAcik = !hoverBilgiAcik; }); komut("Görünüm", "Bağlam İpucu (HUD)", "", [&](Cekirdek&) { ort.hoverHudAcik = !ort.hoverHudAcik; }); komut("Görünüm", "Perf HUD (FPS/Draw-call)", "", [](Cekirdek&) { perfGosterge().hudAcik = !perfGosterge().hudAcik; }); komut("Makro", "Makro Konsolu", "◈", [&](Cekirdek&) { panelMakro = true; }); komut("Makro", "Eklenti Yöneticisi", "◱", [&](Cekirdek&) { panelEklenti = true; }); // ---- CAM (yalnız CAM workbench'te; komutlar undo/redo'lu Islem yolundan) ---- const auto camEkle = [&](cam::CamIslemTip tip) { camSis.acik = true; cekirdek.uygula(std::make_unique(camSis, tip)); if (cam::CamIslem* op = camSis.bul((u32)camSis.seciliKimlik)) { camSis.geometridenAta(*op); camSis.pencereAc(op->kimlik); // yeni operasyon kendi penceresiyle acilir } }; komut("CAM", "Profil", "▭", [&, camEkle](Cekirdek&) { camEkle(cam::CamIslemTip::Profil); }, "cam", etkinBrepli); komut("CAM", "Cep", "▣", [&, camEkle](Cekirdek&) { camEkle(cam::CamIslemTip::Cep); }, "cam", etkinBrepli); komut("CAM", "Delik", "◉", [&, camEkle](Cekirdek&) { camEkle(cam::CamIslemTip::Delik); }, "cam", etkinBrepli); komut("CAM", "3D Kaba", "≋", [&, camEkle](Cekirdek&) { camEkle(cam::CamIslemTip::Kaba3D); }, "cam", etkinBrepli); komut("CAM", "Kabadan Kalan", "⧉", [&, camEkle](Cekirdek&) { camEkle(cam::CamIslemTip::RestKaba); }, "cam", etkinBrepli); komut("CAM", "3D Finiş", "≡", [&, camEkle](Cekirdek&) { camEkle(cam::CamIslemTip::Finis3D); }, "cam", etkinBrepli); komut("CAM", "Sweep Finiş", "↝", [&, camEkle](Cekirdek&) { camEkle(cam::CamIslemTip::SweepFinis); }, "cam", etkinBrepli); komut("CAM", "Advanced Finiş", "◈", [&, camEkle](Cekirdek&) { camEkle(cam::CamIslemTip::AdvancedFinis); }, "cam", etkinBrepli); komut("CAM", "Kontur Güdümlü", "◠", [&, camEkle](Cekirdek&) { camEkle(cam::CamIslemTip::KonturGudumlu); }, "cam", etkinBrepli); komut("CAM", "Kalan Finiş (Pencil)", "✎", [&, camEkle](Cekirdek&) { camEkle(cam::CamIslemTip::ReworkFinis); }, "cam", etkinBrepli); komut("CAM", "⭳ G-Kod (NC)", "⭳", [&](Cekirdek&) { fiko::gkodAktar(bglm); }, "cam"); komut("CAM", "Tümünü Hesapla", "⟳", [&](Cekirdek&) { camSis.kirlileriHesapla(); }, "cam"); komut("CAM", "🎬 Simülasyon", "🎬", [&](Cekirdek&) { camSimPencereAc(0); }, "cam"); komut("CAM", "⭳ APT Aktar", "⭳", [&](Cekirdek&) { fiko::aptAktar(bglm); }, "cam"); komut("CAM", "🔧 Takım Tanımla", "🔧", [&](Cekirdek&) { // Yeni varsayilan takim ekle (numara = en buyuk + 1) → grafiksel pencerede ac. auto& lst = camSis.takimlar(); int no = 1; for (const cam::Takim& t : lst) no = std::max(no, t.numara + 1); cam::Takim yeni; yeni.numara = no; yeni.ad = "Parmak Freze Ø10"; yeni.boy = yeni.cap * 3.0; yeni.toplamBoy = yeni.cap * 6.0; lst.push_back(yeni); camTakimPencereAc(no); }, "cam"); // ---- CATIA modeli (yeni CamOperasyon/PartOperasyon; her komut kendi penceresinde) ---- const auto catOpEkle = [&](cam::CamIslemTipCatia tip) { panelAgac = true; // CAM düğümleri ana Ürün Ağacı'nda görünür const u32 k = camSis.opEkle(tip); camOperasyonPencereAc(k); // op kendi penceresini açar }; komut("CAM", "◫ Part Operation Ekle", "▤", [&](Cekirdek&) { panelAgac = true; const u32 k = camSis.parcaOpEkle(); camParcaOpPencereAc(k); // makine ayarları penceresi açılır }, "cam"); komut("CAM", "Üretim Ağacı", "🗂", [&](Cekirdek&) { panelAgac = true; }, "cam"); komut("CAM", "Yüzey Tarama (Facing)", "▱", [&, catOpEkle](Cekirdek&) { catOpEkle(cam::CamIslemTipCatia::Facing); }, "cam"); komut("CAM", "Cep (Pocketing)", "▣", [&, catOpEkle](Cekirdek&) { catOpEkle(cam::CamIslemTipCatia::Pocketing); }, "cam"); komut("CAM", "Profil (Contouring)", "▭", [&, catOpEkle](Cekirdek&) { catOpEkle(cam::CamIslemTipCatia::Contouring); }, "cam"); komut("CAM", "Eğri Takip", "◠", [&, catOpEkle](Cekirdek&) { catOpEkle(cam::CamIslemTipCatia::CurveFollow); }, "cam"); komut("CAM", "Dairesel Freze", "◎", [&, catOpEkle](Cekirdek&) { catOpEkle(cam::CamIslemTipCatia::CircularMill); }, "cam"); komut("CAM", "Delme (Drilling)", "◉", [&, catOpEkle](Cekirdek&) { catOpEkle(cam::CamIslemTipCatia::Drilling); }, "cam"); komut("CAM", "Punta (Spot Drill)", "⊙", [&, catOpEkle](Cekirdek&) { catOpEkle(cam::CamIslemTipCatia::SpotDrill); }, "cam"); komut("CAM", "Derin Delik (Peck)", "⇟", [&, catOpEkle](Cekirdek&) { catOpEkle(cam::CamIslemTipCatia::DeepHole); }, "cam"); komut("CAM", "Kılavuz (Tapping)", "⎇", [&, catOpEkle](Cekirdek&) { catOpEkle(cam::CamIslemTipCatia::Tapping); }, "cam"); komut("CAM", "Rayba (Reaming)", "⌾", [&, catOpEkle](Cekirdek&) { catOpEkle(cam::CamIslemTipCatia::Reaming); }, "cam"); // ---- Sketcher ---- (Part'ta düzlem/düz yüzey seç → Sketch Oluştur; Sketcher'da Çık) // CATIA: seçili öğe bir REFERANS düzlem (DugumTip::Duzlem) ya da bir katının DÜZ // (planar) YÜZÜ olabilir. Düz yüzeyden geçici referans düzlem türetilir. // Dönüş: sketch düzlemi Varlik'i (yoksa VARLIK_YOK). XY'ye otomatik düşmez. const auto sketchDuzlemBul = [&]() -> Varlik { sketchDurum.sketchKaynakGovde = VARLIK_YOK; // düz yüzden türetilirse aşağıda set edilir (DM it/çek hedefi) // 1) Doğrudan seçili referans düzlem. for (u32 g : sahne.seciliGovdeler()) if (Dugum* d = sahne.bul(g); d && d->tip == DugumTip::Duzlem) return g; // 1b) CATIA Eksen Sistemi L-köşesi seçildiyse (alt 4/5/6 = XY/YZ/ZX düzlemi): // gerçek referans DÜZLEM türet (sketchEkle bir DugumTip::Duzlem Varlik bekler). for (const SecimOge& o : sahne.secililer()) { Dugum* d = sahne.bul(o.govde); if (d && d->tip == DugumTip::EksenSistemi && eksensis::duzlemMi(o.alt)) return sahne.eksenSistemiDuzlemi(o.alt); } // 2) Seçili düz yüzey (SecimOge: govde + alt=yüz ordinali, yuzBilgi.duzlemMi). for (const SecimOge& o : sahne.secililer()) { if (o.alt == 0) continue; // yüz/kenar/nokta ögesi değil (gövde) Dugum* d = sahne.bul(o.govde); if (!d || !d->brep || !d->brep->gecerliMi()) continue; geometri::YuzBilgi yb; if (d->brep->yuzBilgi(o.alt, yb)) { // düz VEYA eğri yüz (eğride UV-ortası teğet düzlem) sketchDurum.sketchKaynakGovde = o.govde; // it/çek Pad/Cep bu gövdeye uygulanacak return sahne.yuzdenDuzlemEkle(o.govde, o.alt); } } // 3) Sağ-tık "Eskiz Aç": öğe SEÇİLİ değil → bağlam menüsü hedefi (govde + alt ordinali). // Referans düzlem / Origin eksen-sistemi düzlemi (alt 4/5/6) / düz yüz kabul edilir. if (sonPolitikaHedef) { if (Dugum* d = sahne.bul(sonPolitikaHedef)) { if (d->tip == DugumTip::Duzlem) return sonPolitikaHedef; if (d->tip == DugumTip::EksenSistemi && eksensis::duzlemMi(sonPolitikaHedefAlt)) return sahne.eksenSistemiDuzlemi(sonPolitikaHedefAlt); if (d->brep && d->brep->gecerliMi() && sonPolitikaHedefAlt) { geometri::YuzBilgi yb; if (d->brep->yuzBilgi(sonPolitikaHedefAlt, yb)) { // düz VEYA eğri yüz sketchDurum.sketchKaynakGovde = sonPolitikaHedef; // it/çek Pad/Cep hedefi return sahne.yuzdenDuzlemEkle(sonPolitikaHedef, sonPolitikaHedefAlt); } } } } return VARLIK_YOK; }; // sketchDuzlemBul kur()-yerel → BY-VALUE yakala (iç [&]'leri kararlı member; kur() // sonrası kareCiz'de çağrılınca dangling YOK; bglm alanı gerektirmez, [&, geoEkle] deseni). komut("Sketch", "Sketch Oluştur", "✎", [&, sketchDuzlemBul](Cekirdek& c) { const Varlik dz = sketchDuzlemBul(); if (dz == VARLIK_YOK) { bilgiVer("Sketch için düzlemsel bir yüzey veya referans düzlem seçin."); return; } // Sketch'e İLK girişte (zaten sketch modunda DEĞİLKEN) önceki workbench + kamera bakışını // sakla → "Sketch'ten Çık"ta AYNEN geri yüklenir (Normal View kamerayı döndürür, çıkışta geri). if (aktifSketch == VARLIK_YOK) { sketchDurum.sketchOncekiWb = c.workbenchler().aktif(); const Quatf kq = kamera.yonelim(); const Vec3f ko = kamera.odak(); sketchDurum.sketchKamYon[0] = kq.x; sketchDurum.sketchKamYon[1] = kq.y; sketchDurum.sketchKamYon[2] = kq.z; sketchDurum.sketchKamYon[3] = kq.w; sketchDurum.sketchKamOdak[0] = ko.x; sketchDurum.sketchKamOdak[1] = ko.y; sketchDurum.sketchKamOdak[2] = ko.z; sketchDurum.sketchKamMesafe = kamera.mesafe(); sketchDurum.sketchKamFov = kamera.gorusAcisiDeg(); sketchDurum.sketchKamOrtho = kamera.ortografik(); sketchDurum.sketchKamVar = true; } // Zaten açık bir sketch varsa önce onu kapat (araç/seçim durumu sızmasın). if (aktifSketch != VARLIK_YOK) { c.olaylar().yayinla(SketchCikildi{aktifSketch}, "Sketch"); sketchArac.clear(); sketchNoktalar.clear(); sketchSecili.clear(); sketchSeciliKisit.clear(); sketchDuzenFaz = 0; } aktifSketch = sahne.sketchEkle(dz); partD.sketchGizlemeMuafAyarla(aktifSketch); // yeni sketch düzenlemede — oto gizleme muaf // Kaynak gövde bağı (düz yüzden açıldıysa): DM'de sketch'i it/çek → o gövdeye Pad/Cep. // + Türeyen "Yüz Düzlemi" AYRI ağaç düğümü olmasın: sketch ALTINA al + gizle (kullanıcı isteği; // otomatik ayrı nesne oluşmasın). Kullanıcının kendi seçtiği referans düzleme DOKUNULMAZ. if (sketchDurum.sketchKaynakGovde != VARLIK_YOK) { if (NesneKok* nk = sahne.dunya().bul(aktifSketch); nk && *nk) if (auto* sn = dynamic_cast(nk->get())) sn->kaynakGovdeAyarla(sketchDurum.sketchKaynakGovde); sahne.ustAyarla(dz, aktifSketch); // düzlemi sketch altına yuvala if (Dugum* dzD = sahne.bul(dz)) dzD->gorunur = false; // 3B'de gizle (grid yeterli) } c.workbenchler().aktifAyarla(c, "sketcher"); c.olaylar().yayinla(SketchGirildi{aktifSketch}, "Sketch"); panelSketch = true; // CATIA Normal View: kamerayı yeni sketch düzlemine dik + merkeze götür. if (Dugum* dzD = sahne.bul(dz)) { const auto& q = dzD->donusum.yonelim; kamera.yonelimeGit(Quatf{(f32)q.x, (f32)q.y, (f32)q.z, (f32)q.w}); kamera.merkezle({(f32)dzD->donusum.konum.x, (f32)dzD->donusum.konum.y, (f32)dzD->donusum.konum.z}); } bilgiVer("Sketch oluşturuldu — Sketcher workbench aktif"); }, "part"); // ---- Teknik Resim (pafta) ---- // DM: seçili gövde varsa ONDAN üç görünüm kurulur (niyet seçimden okunur), yoksa // boş pafta açılır. Ölçek parça ölçüsünden otomatik (sayfaya sığan en büyük standart). komut("Teknik Resim", "Teknik Resim Oluştur", "📐", [&](Cekirdek& c) { u32 kaynak = 0; Vec3f olcu{0, 0, 0}; for (u32 g : sahne.seciliGovdeler()) { const Dugum* d = sahne.bul(g); if (!d || !d->yerelSinir.gecerliMi()) continue; const Vec3f b = d->yerelSinir.boyut(); if (b.x <= 0 || b.y <= 0 || b.z <= 0) continue; kaynak = g; olcu = b; break; } const Varlik p = sahne.paftaEkle(kaynak && sahne.bul(kaynak) ? ("Teknik Resim — " + sahne.bul(kaynak)->ad) : std::string()); if (kaynak) if (pafta::Pafta* pv = paftaVerisi(sahne.dunya(), p)) pv->ucGorunumKur(kaynak, olcu.x, olcu.y, olcu.z); bglm.paftaGir(p); // VarlikEklendi zaten ağacı tazeler bilgiVer(kaynak ? "Teknik resim oluşturuldu (üç görünüm) — çıkmak için Esc" : "Boş teknik resim oluşturuldu — gövde seçip tekrar deneyin"); }, "part"); komut("Teknik Resim", "Teknik Resimden Çık", "✔", [&](Cekirdek&) { bglm.paftaCik(); }, "part"); komut("Sketch", "Sketch'ten Çık", "✔", [&](Cekirdek& c) { const Varlik cikilan = aktifSketch; // it/çek jesti için çıkışta seçili bırakılır if (aktifSketch != VARLIK_YOK) c.olaylar().yayinla(SketchCikildi{aktifSketch}, "Sketch"); aktifSketch = VARLIK_YOK; // Muafiyet biter → yutulmuş sketch ANINDA gizlenir (setter görünürlüğü tazeler). partD.sketchGizlemeMuafAyarla(VARLIK_YOK); sketchArac.clear(); sketchNoktalar.clear(); sketchSecili.clear(); sketchSeciliKisit.clear(); panelSketch = false; // Sketch öncesi bağlamı AYNEN geri yükle: önceki workbench + kamera bakış noktası. const std::string hedefWb = sketchDurum.sketchOncekiWb.empty() ? std::string("dogrudan") : sketchDurum.sketchOncekiWb; c.workbenchler().aktifAyarla(c, hedefWb); sketchDurum.sketchOncekiWb.clear(); if (sketchDurum.sketchKamVar) { kamera.durumAta(Quatf{sketchDurum.sketchKamYon[0], sketchDurum.sketchKamYon[1], sketchDurum.sketchKamYon[2], sketchDurum.sketchKamYon[3]}, Vec3f{sketchDurum.sketchKamOdak[0], sketchDurum.sketchKamOdak[1], sketchDurum.sketchKamOdak[2]}, sketchDurum.sketchKamMesafe, sketchDurum.sketchKamFov, sketchDurum.sketchKamOrtho); sketchDurum.sketchKamVar = false; } // Çıkılan sketch'i seçili bırak → DM'de tut-çek oku hemen belirir (butonsuz Pad/Cep). if (cikilan != VARLIK_YOK) { sahne.secimTemizle(); sahne.sec({cikilan, 0}, false); } }, "sketcher", etkinSketch); // CATIA Sketch Analysis: açık uç / açık profil / izole nokta + kısıt durumu tanısı. komut("Sketch", "Sketch Analizi", "🔬", [&](Cekirdek&) { sketchDurum.sketchAnalizAcik = !sketchDurum.sketchAnalizAcik; }, "sketcher", etkinSketch); // ---- Bağlam-farkındalık meta (Dalga 2): komut → uygun HedefKategori maskesi ---- // Sağ-tık halkası + toolbar bağlam vurgusu bu meta'yı OKUR (TEK kaynak; mükerrer yok). // Yalnız yaygın komutlara verilir; kalanlar maske 0 (global) kalır → geriye uyumlu. { using baglam::HedefKategori; const u32 kenarM = baglam::maske(HedefKategori::KenarCizgi, HedefKategori::KenarYay, HedefKategori::KenarCember, HedefKategori::KenarSpline); const u32 yuzM = baglam::maske(HedefKategori::YuzDuz, HedefKategori::YuzSilindir, HedefKategori::YuzKonik, HedefKategori::YuzKure, HedefKategori::YuzEgri); const auto meta = [&](const char* grup, const char* ad, u32 m, u8 mn = 0, u8 mx = 255) { if (MenuOge* o = menuK.bul(grup, ad)) { o->hedefMaske = m; o->minSecim = mn; o->maxSecim = mx; } }; meta("İşlem", "Kenar Yuvarlat (3B)", kenarM | yuzM); // kenar/yüz seçimine uygulanır meta("İşlem", "Pah (3B)", kenarM | yuzM); meta("Feature", "Pad (Kabartma)", yuzM); // yüz/sketch'ten kabartma meta("Feature", "Pocket (Cep)", yuzM); meta("Feature", "Fillet (Kenar Yuvarlat)", kenarM | yuzM); // kenar seçimine uygulanır meta("Feature", "Chamfer (Pah)", kenarM | yuzM); meta("Feature", "Shell (İçi Boşalt)", yuzM); // açık bırakılacak yüz seçimi meta("Feature", "Draft (Yüz Açısı)", yuzM); meta("Feature", "Dik Dizi", baglam::maske(HedefKategori::Govde)); // katı gövdeyi çoğaltır meta("Feature", "Dairesel Dizi", baglam::maske(HedefKategori::Govde)); meta("Feature", "Ayna (Mirror)", baglam::maske(HedefKategori::Govde)); meta("Sketch", "Sketch Oluştur", baglam::maske(HedefKategori::RefDuzlem, HedefKategori::YuzDuz)); meta("Ölçüm", "Ölçüm Penceresi (M)", baglam::geometriHepsi()); // her geometride ölç meta("Boolean", "Birleştir", baglam::maske(HedefKategori::Govde), 2, 2); // 2 gövde seçili meta("Boolean", "Çıkar (A−B)", baglam::maske(HedefKategori::Govde), 2, 2); meta("Boolean", "Kesişim", baglam::maske(HedefKategori::Govde), 2, 2); } // ---- Recompute tetikleyicileri (parametrik/link zinciri) ---- // Geometri/parametre değişimi bağımlı nesneleri kirletir; kapanış kare sonunda // topolojik sırada çözülür (RecomputeMotor). kirlet(v) O(1); yayılım hesapla()'da. // Öğrenen sıralama: her BAŞARILI komut çalıştırması sayılır. KomutCalisti tek // merkezden (Cekirdek::komutCalistir) yayınlandığı için menü/toolbar/palet/kısayol/ // makro — hepsi tek yerde sayılır; DM arama menüsü "sık kullandıkların" bunu okur. cekirdek.olaylar().abone([](const KomutCalisti& e) { if (!e.basarili || !e.ad || !e.kategori) return; // KomutCalisti bağlam TAŞIMAZ. DM menüsünden çalıştıysa (kaynak "dm_arama") menü // açılırken kaydedilen bağlama yaz → "duruma göre" öğrenme. Başka kaynak // (toolbar/kısayol/palet) bağlamsızdır → "genel" kovası (soğuk başlangıç yedeği). const bool dmden = e.kaynak && std::strcmp(e.kaynak, "dm_arama") == 0; komutKullanimArtir(dmden ? dmBaglam() : std::string("genel"), e.kategori, e.ad); }); // Makro önerisi: ardışık tekrarlanan komut dizisini yakala (bkz. makro_oneri.hpp). cekirdek.olaylar().abone([this](const KomutCalisti& e) { if (!e.basarili || !e.ad || !e.kategori) return; ort_.makroOneri.minTekrar = makroOnerisiMinTekrar(); // ayar anında geçerli ort_.makroOneri.komutIsle(e.kategori, e.ad); }); cekirdek.olaylar().abone( [&](const DonusumDegisti& e) { cekirdek.recompute().kirlet(e.varlik); }); cekirdek.olaylar().abone( [&](const BrepDegisti& e) { cekirdek.recompute().kirlet(e.varlik); }); // Parametre değişince BAĞIMLI feature'lar da kirlenmeli → iki-argümanlı kirlet // (ters kenar BFS: param → onu formülde kullanan feature'lar). Tek-arg yalnız // param düğümünü kirletir, feature'a yayılmazdı (parametrik formül çalışmazdı). cekirdek.olaylar().abone( [&](const ParametreDegisti& e) { cekirdek.recompute().kirlet(cekirdek, e.varlik); }); // CATIA associative update: Sketcher'dan ÇIKINCA, düzenlenen sketch'e bağlı feature // zinciri (Pad + sonrası) yeniden hesaplanır → Pad şekli güncellenir. Sketch düzenleme // (sürükle/geometri/kısıt) tek tek olay yayınlamaz; çıkış tek merkezi tetikleyicidir. cekirdek.olaylar().abone( [&](const SketchCikildi& e) { partD.sketchGuncellendi(e.sketch); }); // K2: Sahnede seçim değiştiğinde ürün ağacı seçili satıra bir kez kaydırsın. cekirdek.olaylar().abone( [&](const SecimDegisti&) { agacKaydirGerek = true; }); // Imlec altindaki dunya noktasi (odak duzlemi mesafesinde isin uzerinde) const auto imlecDunya = [&](f32 px, f32 py) -> Vec3f { const f32 ndcX = 2.0f * px / sonG - 1.0f; const f32 ndcY = 2.0f * py / sonY_ - 1.0f; const Mat4f invVP = (kamera.izdusum(sonEnBoy) * kamera.gorunum()).ters(); const Vec4f yH = invVP * Vec4f{ndcX, ndcY, 1.0f, 1.0f}; // reverse-Z: yakın düzlem = 1 const Vec4f uH = invVP * Vec4f{ndcX, ndcY, 0.0f, 1.0f}; // reverse-Z: uzak düzlem = 0 const Vec3f yakin = yH.xyz() * (1.0f / yH.w); const Vec3f uzak = uH.xyz() * (1.0f / uH.w); const Vec3f dir = (uzak - yakin).birim(); return yakin + dir * kamera.mesafe(); // orto'da kaynak = near nokta (paralel ışın) }; // İmleç ışınını gerçek yüzeye atar; vuruş (gövde + dünya noktası + normal) döner. const auto imlecVurus = [&](f32 px, f32 py, Sahne::IsinVurus& vr) -> bool { const f32 ndcX = 2.0f * px / sonG - 1.0f; const f32 ndcY = 2.0f * py / sonY_ - 1.0f; const Mat4f invVP = (kamera.izdusum(sonEnBoy) * kamera.gorunum()).ters(); const Vec4f yH = invVP * Vec4f{ndcX, ndcY, 1.0f, 1.0f}; // reverse-Z: yakın düzlem = 1 const Vec4f uH = invVP * Vec4f{ndcX, ndcY, 0.0f, 1.0f}; // reverse-Z: uzak düzlem = 0 const Vec3f yakinN = yH.xyz() * (1.0f / yH.w); const Vec3f uzakN = uH.xyz() * (1.0f / uH.w); return sahne.isinVur(yakinN, (uzakN - yakinN).birim(), vr); }; // İmleç altındaki GERÇEK yüzeye ışın atar; vuruş varsa dünya noktasını verir. // Merkezleme (döndürme pivotu) ve tekerlek zoom hedefi için ortak (CATIA hissi). const auto imlecYuzey = [&, imlecVurus](f32 px, f32 py, Vec3f& cikti) -> bool { Sahne::IsinVurus vr; if (imlecVurus(px, py, vr)) { cikti = vr.nokta; return true; } return false; }; // Merkezleme/zoom pivotu (CATIA orta-tık + tekerlek). Öncelik: (1) ışın gerçek yüzeye // vurursa tam nokta (her mesafede en doğru). (2) Vurmaz ama tam pick pipeline (GPU // fallback dâhil) altında bir gövde varsa o gövdenin dünya sınır-kutusu merkezi → pivot // parçanın İÇİNDE kalır (orbit dalmaz) + uzak parça CPU BVH kurulmamışken bile doğru // merkezlenir. false = imleç altında ne yüzey ne gövde (gerçek boşluk). const auto imlecMerkez = [&, imlecYuzey](f32 px, f32 py, Vec3f& cikti) -> bool { if (imlecYuzey(px, py, cikti)) return true; if (sahne.vurgu.govde) if (const Dugum* d = sahne.bul(sahne.vurgu.govde)) { cikti = d->dunyaMerkez(); return true; } // Çok uzakta (async BVH kurulmamış + hover boş): ışın-küre ile en yakın katı gövde → // üçgen taraması olmadan (O(gövde)) her mesafede merkezleme tutar. const f32 ndcX = 2.0f * px / sonG - 1.0f; const f32 ndcY = 2.0f * py / sonY_ - 1.0f; const Mat4f invVP = (kamera.izdusum(sonEnBoy) * kamera.gorunum()).ters(); const Vec4f yH = invVP * Vec4f{ndcX, ndcY, 1.0f, 1.0f}; // reverse-Z: yakın = 1 const Vec4f uH = invVP * Vec4f{ndcX, ndcY, 0.0f, 1.0f}; // reverse-Z: uzak = 0 const Vec3f yakinN = yH.xyz() * (1.0f / yH.w); const Vec3f uzakN = uH.xyz() * (1.0f / uH.w); return sahne.isinKureEnYakin(yakinN, (uzakN - yakinN).birim(), cikti) != 0; }; // ---- Sketch düzlem eşleme (uv ↔ dünya) ---- // Sketch düzlemin yerel XY'si = eskiz uv (z=0). Düzlem düğümü sahne.bul() ile // alınır; donusum bağlı düzlemin dünya konumu/yönelimidir. const auto sketchNesne = [&]() -> SketchNesnesi* { if (aktifSketch == VARLIK_YOK) return nullptr; return dynamic_cast(cekirdek.nesne(aktifSketch)); }; // K5: sketch seçim havuzu değiştiğinde olay yayınla → makro/plugin gözlemleyebilir // (anayasa: her seçim yolu olay üretir). sketchSecili main-yerel olduğundan yayın // burada, mutasyon noktalarından çağrılır. const auto sketchSecimYay = [&] { cekirdek.olaylar().yayinla( SketchSecimDegisti{aktifSketch, static_cast(sketchSecili.size())}, "Sketch"); }; // CATIA "Normal View": kamerayı sketch düzlemine dik + merkez konumuna götür. // Kamera yönelimi = düzlem yönelimi → sketch XY'sine tam karşıdan bakılır. const auto sketchDikBak = [&](Varlik s) { auto* sk = dynamic_cast(cekirdek.nesne(s)); if (!sk) return; if (Dugum* dz = sahne.bul(sk->duzlem())) { const auto& q = dz->donusum.yonelim; kamera.yonelimeGit(Quatf{(f32)q.x, (f32)q.y, (f32)q.z, (f32)q.w}); kamera.merkezle({(f32)dz->donusum.konum.x, (f32)dz->donusum.konum.y, (f32)dz->donusum.konum.z}); } }; // Bir sketch'e gir (ürün ağacı/modüller çağırır): önceki açıksa kapat + araç // durumu temizle, sketcher workbench'e geç, Normal View. (Eski ürün-ağacı // çift-tık gövdesi buraya kapsüllendi — davranış birebir.) bglm.sketchGir = [&, sketchDikBak](Varlik v) { // sketchDikBak yerel lambda → BY-VALUE if (aktifSketch != VARLIK_YOK && aktifSketch != v) { cekirdek.olaylar().yayinla(SketchCikildi{aktifSketch}, "Sketch"); sketchArac.clear(); sketchNoktalar.clear(); sketchSecili.clear(); sketchDuzenFaz = 0; } aktifSketch = v; // Düzenlenen sketch oto-gizlemeden muaf + yutulmuş (gizli) sketch düzenleme için görünür. partD.sketchGizlemeMuafAyarla(v); if (Dugum* sd = sahne.bul(v)) sd->gorunur = true; cekirdek.workbenchler().aktifAyarla(cekirdek, "sketcher"); cekirdek.olaylar().yayinla(SketchGirildi{v}, "Sketch"); panelSketch = true; sketchDikBak(v); }; // ---- Teknik resim (pafta) moduna gir/çık ---- // DM tarzı: toolbar YOK. Ağaçta pafta satırına çift tık ile girilir, Esc ile çıkılır. // 3B bağlam (workbench + kamera) girişte yedeklenir, çıkışta AYNEN geri yüklenir // (sketch gir/çık kalıbının aynısı; sayfa çizimi adım 3). bglm.paftaGir = [&](Varlik v) { if (!paftaVerisi(sahne.dunya(), v)) return; // pafta olmayan düğüm → sessiz if (aktifPafta != VARLIK_YOK && aktifPafta != v) cekirdek.olaylar().yayinla(PaftaCikildi{aktifPafta}, "Teknik Resim"); if (aktifPafta == VARLIK_YOK) { // İLK giriş: 3B bağlamı yedekle paftaDurum.oncekiWb = cekirdek.workbenchler().aktif(); const Quatf kq = kamera.yonelim(); const Vec3f ko = kamera.odak(); paftaDurum.kamYon[0] = kq.x; paftaDurum.kamYon[1] = kq.y; paftaDurum.kamYon[2] = kq.z; paftaDurum.kamYon[3] = kq.w; paftaDurum.kamOdak[0] = ko.x; paftaDurum.kamOdak[1] = ko.y; paftaDurum.kamOdak[2] = ko.z; paftaDurum.kamMesafe = kamera.mesafe(); paftaDurum.kamFov = kamera.gorusAcisiDeg(); paftaDurum.kamOrtho = kamera.ortografik(); paftaDurum.kamVar = true; } aktifPafta = v; paftaDurum.sayfaPanX = paftaDurum.sayfaPanY = 0.0f; paftaDurum.sayfaZoom = 1.0f; cekirdek.olaylar().yayinla(PaftaGirildi{v}, "Teknik Resim"); kareIste(2); }; bglm.paftaAktifMi = [&](Varlik v) { return aktifPafta != VARLIK_YOK && aktifPafta == v; }; bglm.paftaCik = [&] { if (aktifPafta == VARLIK_YOK) return; cekirdek.olaylar().yayinla(PaftaCikildi{aktifPafta}, "Teknik Resim"); aktifPafta = VARLIK_YOK; // Workbench DEĞİŞTİRİLMEZ (DM: tek tezgah). Girişte de değiştirmiyoruz; çıkışta // geri yükleme kullanıcının kendi seçimini ezerdi. if (paftaDurum.kamVar) { // 3B bakış aynen geri kamera.yonelimeGit(Quatf{paftaDurum.kamYon[0], paftaDurum.kamYon[1], paftaDurum.kamYon[2], paftaDurum.kamYon[3]}); kamera.merkezle({paftaDurum.kamOdak[0], paftaDurum.kamOdak[1], paftaDurum.kamOdak[2]}); paftaDurum.kamVar = false; } kareIste(2); }; // Düzlem-yerel (u,v) → dünya noktası (z=0 düzleminde). const auto uvDunya = [&](const Dugum* dz, f64 u, f64 v) -> Vec3f { const Vec3d w = dz->donusum.noktaUygula({u, v, 0.0}); return {(f32)w.x, (f32)w.y, (f32)w.z}; }; // İmleç ışınını sketch düzlemiyle kes → düzlem-yerel (u,v). Işın imlecDunya // desenindeki gibi kurulur (ndc → invVP → yakın/uzak). const auto ekranUV = [&](const Dugum* dz, f32 px, f32 py, f64& u, f64& v) -> bool { const f32 ndcX = 2.0f * px / sonG - 1.0f; const f32 ndcY = 2.0f * py / sonY_ - 1.0f; const Mat4f invVP = (kamera.izdusum(sonEnBoy) * kamera.gorunum()).ters(); const Vec4f yH = invVP * Vec4f{ndcX, ndcY, 1.0f, 1.0f}; // reverse-Z: yakın düzlem = 1 const Vec4f uH = invVP * Vec4f{ndcX, ndcY, 0.0f, 1.0f}; // reverse-Z: uzak düzlem = 0 const Vec3f yakin = yH.xyz() * (1.0f / yH.w); const Vec3f uzak = uH.xyz() * (1.0f / uH.w); const Vec3f dir = (uzak - yakin).birim(); const Vec3d nd = dz->donusum.yonelim.dondur({0, 0, 1}); const Vec3f normal{(f32)nd.x, (f32)nd.y, (f32)nd.z}; const Vec3f duzP{(f32)dz->donusum.konum.x, (f32)dz->donusum.konum.y, (f32)dz->donusum.konum.z}; const f32 payda = normal.nokta(dir); if (std::abs(payda) < 1e-6f) return false; // ışın düzleme paralel const f32 t = (duzP - yakin).nokta(normal) / payda; if (t < 0.0f) return false; // düzlem arkada const Vec3f P = yakin + dir * t; const Vec3d L = dz->donusum.ters().noktaUygula({(f64)P.x, (f64)P.y, (f64)P.z}); u = L.x; v = L.y; return true; }; // Geometrinin uç noktası sayısı (coincidence taraması için: uc 0/1). const auto sketchUcSayi = [&](const eskiz::Geometri& g) -> int { if (g.tip == eskiz::GeoTip::Cizgi) return 2; // Yay/Spline uçları a/b'de GERÇEK solver noktaları (07-09 yay uç fix'i + spline // ayna tasarımı) → oto-çakışık güvenli; solver Cakisik'i noktaSec ile zaten çözer // (test: cakisik_yay_ucu). Ayar kapalı = eski davranış (yay/spline uçsuz). if ((g.tip == eskiz::GeoTip::Yay || g.tip == eskiz::GeoTip::Spline) && sketchDurum.sketchOtoCakisikYayUc) return 2; if (g.tip == eskiz::GeoTip::Nokta || g.tip == eskiz::GeoTip::Daire) return 1; return 0; // yay/spline uçları ayar kapalıyken atlanır }; // Coincidence kısıtı zaten var mı (aynı geo/uç çifti, sıra bağımsız). const auto sketchCakisikVar = [&](eskiz::EskizVeri& ev, u32 a, int au, u32 b, int bu) { for (const eskiz::Kisit& k : ev.kisitlar()) { if (k.tip != eskiz::KisitTip::Cakisik || k.geo.size() != 2) continue; const int k0 = k.nokta.size() > 0 ? k.nokta[0] : 0; const int k1 = k.nokta.size() > 1 ? k.nokta[1] : 0; if ((k.geo[0] == a && k0 == au && k.geo[1] == b && k1 == bu) || (k.geo[0] == b && k0 == bu && k.geo[1] == a && k1 == au)) return true; } return false; }; // Yeni eklenen geometrinin uçları başka uçlara < tol yakınsa oto Çakışık kısıt. // Oto-kısıt STABİL ekleme: kısıt eklendikten sonra çözüm Çelişkili/Aşırı olursa // eklenen kısıt geri alınır → "kendi kendine bozan" oto-kısıt önlenir (profesyonel). const auto sketchGuvenliKisit = [](eskiz::EskizVeri& ev, u32 kimlik) { if (!kimlik) return; const auto d = ev.coz().durum; if (d == eskiz::Solver::Durum::Celiskili || d == eskiz::Solver::Durum::AsiriKisitli) { ev.kisitSil(kimlik); ev.coz(); } }; const auto sketchOtoCakisik = [&, sketchUcSayi, sketchCakisikVar, sketchGuvenliKisit]( eskiz::EskizVeri& ev, u32 yeni) { if (!sketchOtoGeoKisit) return; const eskiz::Geometri* yg = ev.geoBul(yeni); if (!yg) return; const f64 tol = sketchDurum.sketchOtoCakisikTol; // yalnız GERÇEKTEN çakışık uçlar (AyarSistemi: sketcher.oto_cakisik_tol) const int yn = sketchUcSayi(*yg); for (int i = 0; i < yn; ++i) { const f64 yx = ev.noktaX(yeni, i), yy = ev.noktaY(yeni, i); for (const eskiz::Geometri& o : ev.geometriler()) { if (o.kimlik == yeni) continue; const int on = sketchUcSayi(o); for (int j = 0; j < on; ++j) { const f64 ox = ev.noktaX(o.kimlik, j), oy = ev.noktaY(o.kimlik, j); if (std::hypot(yx - ox, yy - oy) < tol && !sketchCakisikVar(ev, yeni, i, o.kimlik, j)) sketchGuvenliKisit(ev, ev.kisitEkle(eskiz::KisitTip::Cakisik, {yeni, o.kimlik}, {i, j}, 0)); } } } }; // Yeni çizgi neredeyse yatay/dikey ise otomatik Yatay/Dikey kısıt (CATIA auto-constraint): // snap görsel hizalıyor ama kısıtsız çizgi sürüklenince bozulur → parametrik kalsın. const auto sketchOtoHV = [&, sketchGuvenliKisit](eskiz::EskizVeri& ev, u32 yeni) { if (!sketchOtoGeoKisit) return; const eskiz::Geometri* yg = ev.geoBul(yeni); if (!yg || yg->tip != eskiz::GeoTip::Cizgi || yg->yapi) return; const f64 dx = ev.noktaX(yeni, 1) - ev.noktaX(yeni, 0); const f64 dy = ev.noktaY(yeni, 1) - ev.noktaY(yeni, 0); const f64 uz = std::hypot(dx, dy); if (uz < sketchDurum.sketchOtoMinUzunluk) return; // çok kısa segmentlere H/V ekleme (AyarSistemi: sketcher.oto_min_uzunluk) const f64 esik = sketchDurum.sketchOtoHVEsik; // ~2° (sin) tolerans (AyarSistemi: sketcher.oto_hv_esik) if (std::abs(dy) / uz < esik) sketchGuvenliKisit(ev, ev.kisitEkle(eskiz::KisitTip::Yatay, {yeni}, {}, 0)); else if (std::abs(dx) / uz < esik) sketchGuvenliKisit(ev, ev.kisitEkle(eskiz::KisitTip::Dikey, {yeni}, {}, 0)); }; // Yeni geometriye construction bayrağı + oto coincidence + oto H/V uygula. const auto sketchYeniEleman = [&, sketchOtoCakisik, sketchOtoHV](eskiz::EskizVeri& ev, u32 kimlik) { if (!kimlik) return; if (sketchYapiMod) ev.yapiAyarla(kimlik, true); sketchOtoCakisik(ev, kimlik); sketchOtoHV(ev, kimlik); }; // sketchNoktalar biriktirilen tıklardan araç geometrisini eskiz'e ekler. const auto sketchAracTamamla = [&, sketchYeniEleman](eskiz::EskizVeri& ev) { auto& np = sketchNoktalar; // Spline: çift-tıkla bitirilir → çift-tıkın iki tık olayı SON noktayı iki kez // eklemiş olabilir; yakın-duplicate ayıklanır (1e-9 sketch mm). if (sketchArac == "spline") { // TEK duplicate (çift-tık) kırpılır. Eskiden `while` idi: snap aynı noktaya // birden çok kez denk gelirse (ör. mevcut bir geometri/köşeye snap) GERÇEK // tıklanmış noktaları da yiyip son 1-2 noktayı sessizce siliyordu — "sağ // tıkta son noktalar iptal oluyor" şikayeti buradan geliyordu. if (np.size() >= 2 && std::hypot(np.back()[0] - np[np.size() - 2][0], np.back()[1] - np[np.size() - 2][1]) < 1e-9) np.pop_back(); if (np.size() >= 2) { std::vector> gecis; gecis.reserve(np.size()); for (const auto& q : np) gecis.emplace_back(q[0], q[1]); sketchYeniEleman(ev, ev.splineEkle(gecis)); } ev.coz(); np.clear(); return; } else if (sketchArac == "nurbsEgri") { // NURBS Egri completion: use splineNurbsEkle for NURBS curve. // (bkz. spline dalındaki not: en fazla TEK trailing-duplicate kırpılır.) if (np.size() >= 2 && std::hypot(np.back()[0] - np[np.size() - 2][0], np.back()[1] - np[np.size() - 2][1]) < 1e-9) np.pop_back(); if (np.size() >= 2) { std::vector> gecis; gecis.reserve(np.size()); for (const auto& q : np) gecis.emplace_back(q[0], q[1]); sketchYeniEleman(ev, ev.splineNurbsEkle(gecis)); // TEŞHİS: kaç nokta ile oluşturulduğunu bildir — "en fazla 2 çizgi/düz" // şikayetinin gerçek veri kaybından mı yoksa görsel bir şeyden mi // kaynaklandığını netleştirmek için (bir sonraki tur bu sayıyı okusun). bilgiVer("NURBS: " + std::to_string(gecis.size()) + " nokta ile oluşturuldu"); } ev.coz(); np.clear(); return; } if (sketchArac == "cizgi" && np.size() == 2) { sketchYeniEleman(ev, ev.cizgiEkle(np[0][0], np[0][1], np[1][0], np[1][1])); } else if (sketchArac == "dikdortgen" && np.size() == 2) { const f64 x0 = np[0][0], y0 = np[0][1], x1 = np[1][0], y1 = np[1][1]; sketchYeniEleman(ev, ev.cizgiEkle(x0, y0, x1, y0)); sketchYeniEleman(ev, ev.cizgiEkle(x1, y0, x1, y1)); sketchYeniEleman(ev, ev.cizgiEkle(x1, y1, x0, y1)); sketchYeniEleman(ev, ev.cizgiEkle(x0, y1, x0, y0)); } else if ((sketchArac == "daire" || sketchArac == "daireKoord") && np.size() == 2) { const f64 dx = np[1][0] - np[0][0], dy = np[1][1] - np[0][1]; sketchYeniEleman(ev, ev.daireEkle(np[0][0], np[0][1], std::sqrt(dx * dx + dy * dy))); } else if ((sketchArac == "nokta" || sketchArac == "noktaKoord") && np.size() == 1) { sketchYeniEleman(ev, ev.noktaEkle(np[0][0], np[0][1])); } else if (sketchArac == "daire3" && np.size() == 3) { // 3 noktadan geçen çember: iki kenarın orta-dikmeleri kesişimi = merkez. const f64 ax = np[0][0], ay = np[0][1], bx = np[1][0], by = np[1][1]; const f64 cx = np[2][0], cy = np[2][1]; const f64 d = 2.0 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by)); if (std::abs(d) < 1e-9) { sketchNoktalar.clear(); bilgiVer("Noktalar doğrusal — çember yok"); return; } const f64 a2 = ax * ax + ay * ay, b2 = bx * bx + by * by, c2 = cx * cx + cy * cy; const f64 mx = (a2 * (by - cy) + b2 * (cy - ay) + c2 * (ay - by)) / d; const f64 my = (a2 * (cx - bx) + b2 * (ax - cx) + c2 * (bx - ax)) / d; sketchYeniEleman(ev, ev.daireEkle(mx, my, std::hypot(ax - mx, ay - my))); } else if (sketchArac == "yay3" && np.size() == 3) { // 3 nokta: başlangıç / bitiş / üzerinden. Aynı çember matematiği + açılar. const f64 ax = np[0][0], ay = np[0][1], bx = np[1][0], by = np[1][1]; const f64 cx = np[2][0], cy = np[2][1]; const f64 d = 2.0 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by)); if (std::abs(d) < 1e-9) { sketchNoktalar.clear(); bilgiVer("Noktalar doğrusal — yay yok"); return; } const f64 a2 = ax * ax + ay * ay, b2 = bx * bx + by * by, c2 = cx * cx + cy * cy; const f64 mx = (a2 * (by - cy) + b2 * (cy - ay) + c2 * (ay - by)) / d; const f64 my = (a2 * (cx - bx) + b2 * (ax - cx) + c2 * (bx - ax)) / d; const f64 r = std::hypot(ax - mx, ay - my); f64 a0 = std::atan2(ay - my, ax - mx); // başlangıç açısı f64 a1 = std::atan2(by - my, bx - mx); // bitiş açısı const f64 am = std::atan2(cy - my, cx - mx); // üzerinden nokta açısı // a0→a1 arasında (CCW) am düşecek şekilde yönü seç. auto norm = [](f64 a) { constexpr f64 tau = 2.0 * std::numbers::pi; while (a < 0) a += tau; while (a >= tau) a -= tau; return a; }; const f64 dA1 = norm(a1 - a0), dAm = norm(am - a0); if (dAm > dA1) std::swap(a0, a1); // am aralık dışı → ters yön (a1→a0) sketchYeniEleman(ev, ev.yayEkle(mx, my, r, a0, a1)); } else if (sketchArac == "merkezYay" && np.size() == 3) { // Merkez-tabanlı yay (CATIA Arc): 1=merkez, 2=başlangıç (yarıçap+açı), // 3=bitiş açısı. yay3'ten FARKLI (o 3-noktadan-geçen). Süpürme CCW. const f64 cx = np[0][0], cy = np[0][1]; const f64 r = std::hypot(np[1][0] - cx, np[1][1] - cy); if (r < 1e-9) { sketchNoktalar.clear(); bilgiVer("Yay yarıçapı sıfır"); return; } const f64 a0 = std::atan2(np[1][1] - cy, np[1][0] - cx); f64 a1 = std::atan2(np[2][1] - cy, np[2][0] - cx); constexpr f64 tau = 2.0 * std::numbers::pi; while (a1 <= a0) a1 += tau; // başlangıç→bitiş pozitif (CCW) süpürme sketchYeniEleman(ev, ev.yayEkle(cx, cy, r, a0, a1)); } else if (sketchArac == "elips" && np.size() == 3) { // 1: merkez, 2: büyük eksen ucu (a + dönme), 3: küçük eksen (merkeze dik mesafe). const f64 cx = np[0][0], cy = np[0][1]; const f64 ex = np[1][0] - cx, ey = np[1][1] - cy; const f64 a = std::hypot(ex, ey); const f64 aci = std::atan2(ey, ex); // 3. tık'ın büyük eksene DİK mesafesi = b (|perp bileşeni|). const f64 px = np[2][0] - cx, py = np[2][1] - cy; const f64 b = std::abs(-px * std::sin(aci) + py * std::cos(aci)); if (a < 1e-6 || b < 1e-6) { sketchNoktalar.clear(); bilgiVer("Elips ekseni sıfır"); return; } sketchYeniEleman(ev, ev.elipsEkle(cx, cy, a, b, aci)); } else if (sketchArac == "elipsYay" && np.size() == 5) { // CATIA Eliptik Yay: 1=merkez, 2=büyük eksen ucu (a+dönme), 3=küçük eksen (dik // mesafe = b), 4=başlangıç, 5=bitiş. Açılar PARAMETRİK (yerel u/a, v/b atan2 — // gerçek polar değil; eliptikYayEkle sözleşmesi), süpürme CCW (merkezYay kalıbı). const f64 cx = np[0][0], cy = np[0][1]; const f64 ex = np[1][0] - cx, ey = np[1][1] - cy; const f64 a = std::hypot(ex, ey); const f64 aci = std::atan2(ey, ex); const f64 px = np[2][0] - cx, py = np[2][1] - cy; const f64 b = std::abs(-px * std::sin(aci) + py * std::cos(aci)); if (a < 1e-6 || b < 1e-6) { sketchNoktalar.clear(); bilgiVer("Elips ekseni sıfır"); return; } const auto paramAci = [&](const auto& n) { const f64 dx = n[0] - cx, dy = n[1] - cy; const f64 u = dx * std::cos(aci) + dy * std::sin(aci); const f64 v = -dx * std::sin(aci) + dy * std::cos(aci); return std::atan2(v / b, u / a); }; const f64 t0 = paramAci(np[3]); f64 t1 = paramAci(np[4]); constexpr f64 tau = 2.0 * std::numbers::pi; while (t1 <= t0) t1 += tau; sketchYeniEleman(ev, ev.eliptikYayEkle(cx, cy, a, b, aci, t0, t1)); } else if (sketchArac == "slot" && np.size() == 3) { // 1-2: eksen merkezleri, 3: genişlik (2. merkeze uzaklık = yarıçap). const f64 r = std::hypot(np[2][0] - np[1][0], np[2][1] - np[1][1]); if (r < 1e-6) { sketchNoktalar.clear(); bilgiVer("Slot yarıçapı sıfır"); return; } for (u32 k : ev.slotEkle(np[0][0], np[0][1], np[1][0], np[1][1], r)) sketchYeniEleman(ev, k); } else if (sketchArac == "egriselSlot" && np.size() == 4) { // CATIA Cylindrical Elongated Hole: 1=merkez, 2=yay başlangıcı (r+a0), // 3=yay bitişi (a1, CCW), 4=genişlik noktası (eksene radyal uzaklık = g). const f64 cx = np[0][0], cy = np[0][1]; const f64 r = std::hypot(np[1][0] - cx, np[1][1] - cy); if (r < 1e-6) { sketchNoktalar.clear(); bilgiVer("Eksen yayı yarıçapı sıfır"); return; } const f64 a0 = std::atan2(np[1][1] - cy, np[1][0] - cx); f64 a1 = std::atan2(np[2][1] - cy, np[2][0] - cx); constexpr f64 tau = 2.0 * std::numbers::pi; while (a1 <= a0) a1 += tau; // başlangıç→bitiş pozitif (CCW) süpürme const f64 g = std::abs(std::hypot(np[3][0] - cx, np[3][1] - cy) - r); if (g < 1e-6 || g >= r) { sketchNoktalar.clear(); bilgiVer("Genişlik geçersiz (0 < g < eksen yarıçapı)"); return; } for (u32 k : ev.egriselSlotEkle(cx, cy, r, a0, a1, g)) sketchYeniEleman(ev, k); } else if (sketchArac == "anahtarDeligi" && np.size() == 3) { // CATIA Keyhole Profile: 1=büyük merkez, 2=küçük merkez, 3=küçük yarıçap // noktası (küçük merkeze uzaklık = r). Büyük R = 2×r (CATIA varsayılan). const f64 bxA = np[0][0], byA = np[0][1], kxA = np[1][0], kyA = np[1][1]; const f64 rK = std::hypot(np[2][0] - kxA, np[2][1] - kyA); if (rK < 1e-6) { sketchNoktalar.clear(); bilgiVer("Küçük yarıçap sıfır"); return; } const f64 RB = 2.0 * rK; const f64 d = std::hypot(kxA - bxA, kyA - byA); if (d <= RB - rK + 1e-6) { sketchNoktalar.clear(); bilgiVer("Merkezler çok yakın (daireler iç içe)"); return; } for (u32 k : ev.anahtarDeligiEkle(bxA, byA, kxA, kyA, rK, RB)) sketchYeniEleman(ev, k); } else if (sketchArac == "cokgen" && np.size() == 2) { // 1: merkez, 2: çevrel yarıçap. Kenar sayısı sketchPolyKenar (panel/canlı giriş). const f64 r = std::hypot(np[1][0] - np[0][0], np[1][1] - np[0][1]); if (r < 1e-6) { sketchNoktalar.clear(); bilgiVer("Çokgen yarıçapı sıfır"); return; } const int kn = std::clamp(sketchPolyKenar, 3, 20); for (u32 k : ev.cokgenEkle(np[0][0], np[0][1], r, kn)) sketchYeniEleman(ev, k); } else if (sketchArac == "merkezliDikdortgen" && np.size() == 2) { // 1: merkez, 2: köşe → yarı-en/yarı-boy = |dx|/|dy|. const f64 yEn = std::abs(np[1][0] - np[0][0]), yBoy = std::abs(np[1][1] - np[0][1]); if (yEn < 1e-6 || yBoy < 1e-6) { sketchNoktalar.clear(); bilgiVer("Dikdörtgen boyutu sıfır"); return; } for (u32 k : ev.merkezliDikdortgenEkle(np[0][0], np[0][1], yEn, yBoy)) sketchYeniEleman(ev, k); } else if (sketchArac == "yonluDikdortgen" && np.size() == 3) { // 1-2: taban doğrultusu, 3: yükseklik (tabana işaretli dik mesafe) — CATIA Oriented Rect. const f64 x0 = np[0][0], y0 = np[0][1], x1 = np[1][0], y1 = np[1][1]; const f64 dx = x1 - x0, dy = y1 - y0, L = std::hypot(dx, dy); if (L < 1e-6) { sketchNoktalar.clear(); bilgiVer("Taban sıfır"); return; } const f64 h = (np[2][0] - x0) * (-dy / L) + (np[2][1] - y0) * (dx / L); // sol normal işaretli if (std::abs(h) < 1e-6) { sketchNoktalar.clear(); bilgiVer("Yükseklik sıfır"); return; } for (u32 k : ev.yonluDikdortgenEkle(x0, y0, x1, y1, h)) sketchYeniEleman(ev, k); } else if (sketchArac == "paralelkenar" && np.size() == 3) { // 1: köşe, 2: 1. kenar ucu, 3: 2. kenar ucu — CATIA Parallelogram. const f64 x0 = np[0][0], y0 = np[0][1]; const f64 v1x = np[1][0] - x0, v1y = np[1][1] - y0; const f64 v2x = np[2][0] - x0, v2y = np[2][1] - y0; if (std::hypot(v1x, v1y) < 1e-6 || std::hypot(v2x, v2y) < 1e-6) { sketchNoktalar.clear(); bilgiVer("Kenar sıfır"); return; } for (u32 k : ev.paralelkenarEkle(x0, y0, v1x, v1y, v2x, v2y)) sketchYeniEleman(ev, k); } else if (sketchArac == "sonsuzCizgi" && !np.empty()) { // CATIA Infinite Line (pragmatik): görünür alanı fazlasıyla aşan (±1e4) // YAPI çizgisi (referans için). Mod: 0=Yatay 1=Dikey (1 tık + H/V kısıt), // 2=2 Nokta (nokta + yön). constexpr f64 SL = 1e4; u32 k = 0; if (sketchDurum.sonsuzMod == 2) { if (np.size() < 2) return; const f64 dx = np[1][0] - np[0][0], dy = np[1][1] - np[0][1]; const f64 L = std::hypot(dx, dy); if (L < 1e-9) { sketchNoktalar.clear(); bilgiVer("Yön sıfır"); return; } k = ev.cizgiEkle(np[0][0] - dx / L * SL, np[0][1] - dy / L * SL, np[0][0] + dx / L * SL, np[0][1] + dy / L * SL); } else { const f64 x = np.back()[0], y = np.back()[1]; const bool yatay = sketchDurum.sonsuzMod == 0; k = yatay ? ev.cizgiEkle(x - SL, y, x + SL, y) : ev.cizgiEkle(x, y - SL, x, y + SL); ev.kisitEkle(yatay ? eskiz::KisitTip::Yatay : eskiz::KisitTip::Dikey, {k}, {}, 0); } ev.yapiAyarla(k, true); // sonsuz çizgi = yapı (construction) elemanı } else { return; } ev.coz(); sketchNoktalar.clear(); }; // Araç başına gereken tık sayısı (tamamlanınca geometri eklenir). const auto sketchAracTikSayi = [&]() -> size_t { if (sketchArac == "spline") return (size_t)0x7fffffff; // degisken tik — cift-tik bitirir if (sketchArac == "nurbsEgri") return (size_t)0x7fffffff; // NURBS: degisken tik + cift-tik // değişken tık — çift-tık bitirir if (sketchArac == "nokta" || sketchArac == "noktaKoord") return 1; if (sketchArac == "sonsuzCizgi") return sketchDurum.sonsuzMod == 2 ? 2 : 1; if (sketchArac == "egriselSlot") return 4; if (sketchArac == "elipsYay") return 5; if (sketchArac == "daire3" || sketchArac == "yay3" || sketchArac == "elips" || sketchArac == "slot" || sketchArac == "yonluDikdortgen" || sketchArac == "paralelkenar" || sketchArac == "merkezYay" || sketchArac == "anahtarDeligi") return 3; if (sketchArac.empty()) return 0; return 2; // cizgi / dikdortgen / daire / daireKoord / cokgen / merkezliDikdortgen }; // ---- Sketcher çizim araçları + kısıt komutları ---- // Araç seç → sketchArac ayarla, bekleyen tıkları temizle (Sketcher toolbar'ında // görünür). Kısıt uygula → sketchSecili geometrilere kısıt ekle + çöz. const auto sketchAracSec = [&](const char* arac) { sketchArac = arac; sketchNoktalar.clear(); }; // ---- 2B düzenleme araçlarının hangi kümede olduğunu tanı ---- const auto sketchDuzenMi = [&](const std::string& a) { return a == "trim" || a == "uzat" || a == "bol" || a == "kapat" || a == "hizlikirp" || a == "tumleyen" || a == "fillet" || a == "pah" || a == "ayna" || a == "simetri" || a == "otele" || a == "dondur" || a == "olcekle" || a == "ofset" || a == "aciortay" || a == "tegetcizgi" || a == "kesisimnokta" || a == "izdusumnokta"; }; // Düzenleme fazını 0→1 ilerlet (Enter / ekran "Devam" düğmesi / ön-seçim ile aynı yol). // sketchSeciliMerkez'den sonra atanır; burada yalnız bildirildi (sketchDuzenSec çağırır). std::function sketchDuzenIlerlet; // sketchDuzenSec, sketchDuzenIlerlet'e bağlı (aşağıda atanır) → oradan sonra // tanımlanır ki by-value capture geçerli (kur() sonrası dangling olmasın). std::function sketchDuzenSec; // İmleç uv'sine en yakın eskiz geometrisi (eşik^2 uv birimi). 0 = yok. const auto sketchEnYakin = [&](eskiz::EskizVeri& ev, f64 u, f64 v) -> u32 { u32 enYakin = 0; f64 enMesafe = 0.35 * 0.35; for (const eskiz::Geometri& g : ev.geometriler()) { f64 md; if (g.tip == eskiz::GeoTip::Cizgi) { const f64 ax = ev.noktaX(g.kimlik, 0), ay = ev.noktaY(g.kimlik, 0); const f64 bx = ev.noktaX(g.kimlik, 1), by = ev.noktaY(g.kimlik, 1); const f64 dx = bx - ax, dy = by - ay, L2 = dx * dx + dy * dy; f64 tpar = L2 > 1e-12 ? ((u - ax) * dx + (v - ay) * dy) / L2 : 0.0; tpar = std::clamp(tpar, 0.0, 1.0); const f64 px2 = ax + tpar * dx - u, py2 = ay + tpar * dy - v; md = px2 * px2 + py2 * py2; } else if (g.tip == eskiz::GeoTip::Daire || g.tip == eskiz::GeoTip::Yay) { const f64 cx = ev.noktaX(g.kimlik, 2), cy = ev.noktaY(g.kimlik, 2); const f64 dr = std::sqrt((u - cx) * (u - cx) + (v - cy) * (v - cy)) - ev.px(g.ir); md = dr * dr; } else { const f64 dx = ev.noktaX(g.kimlik, 0) - u, dy = ev.noktaY(g.kimlik, 0) - v; md = dx * dx + dy * dy; } if (md < enMesafe) { enMesafe = md; enYakin = g.kimlik; } } return enYakin; }; // İmleç/koordinat yardımcılarını bglm'e bağla (MDI ayrı-pencere: frame bunlara // ort.bglm üzerinden erişir → per-window belge'ye doğru bağlanır). Ana pencerede // bglm.X == yerel X olduğundan davranış birebir. bglm.imlecDunya = imlecDunya; bglm.imlecYuzey = imlecYuzey; bglm.imlecMerkez = imlecMerkez; bglm.imlecVurus = imlecVurus; bglm.uvDunya = uvDunya; bglm.ekranUV = ekranUV; bglm.sketchEnYakin = sketchEnYakin; bglm.sketchAracTikSayi = sketchAracTikSayi; bglm.sketchAracTamamla = sketchAracTamamla; bglm.sketchSecimYay = sketchSecimYay; // Seçili geometrilerin ağırlık merkezi (otele/döndür/ölçek varsayılan referansı). const auto sketchSeciliMerkez = [&](eskiz::EskizVeri& ev, f64& mx, f64& my) { mx = my = 0; int n = 0; for (u32 k : sketchSecili) { const eskiz::Geometri* g = ev.geoBul(k); if (!g) continue; const int uc = (g->tip == eskiz::GeoTip::Daire || g->tip == eskiz::GeoTip::Yay) ? 3 : 2; for (int i = 0; i < uc; ++i) { mx += ev.noktaX(k, i); my += ev.noktaY(k, i); ++n; } } if (n) { mx /= n; my /= n; } }; // Düzenleme fazını ilerlet: seçim → eksen/referans/değer aşaması (Enter'a gerek yok). sketchDuzenIlerlet = [&, sketchDuzenMi, sketchNesne, sketchSeciliMerkez]() { if (!sketchDuzenMi(sketchArac) || sketchSecili.empty() || sketchDuzenFaz != 0) return; if (sketchArac == "ayna" || sketchArac == "simetri" || sketchArac == "otele" || sketchArac == "dondur" || sketchArac == "olcekle" || sketchArac == "ofset") { sketchDuzenFaz = 1; if (sketchArac == "ayna" || sketchArac == "simetri") bilgiVer("Eksen çizgisini seçin"); else if (sketchArac == "otele") bilgiVer("Referans noktasını tıklayın (veya değer girin)"); else if (sketchArac == "ofset") bilgiVer("Ofset mesafesini girin / Uygula"); else { if (SketchNesnesi* sn = sketchNesne()) sketchSeciliMerkez(sn->eskiz(), sketchDuzenRef[0], sketchDuzenRef[1]); bilgiVer("Değer girin veya Uygula (referans = seçim merkezi)"); } } }; // Düzenleme moduna geç: seçimi koru (ayna/otele önce seçim ister), fazı sıfırla. // sketchDuzenIlerlet by-value yakalanır (artık atanmış + kendine-yeter) → kopyalanınca dangling yok. sketchDuzenSec = [&, sketchDuzenIlerlet](const char* arac) { sketchArac = arac; sketchNoktalar.clear(); sketchDuzenFaz = 0; for (auto& s : sketchDuzGiris) s[0] = 0; if (sketchArac == "ayna") sketchDurum.sketchAynaKopya = true; // Ayna = kopyala (öntanım) else if (sketchArac == "simetri") sketchDurum.sketchAynaKopya = false; // Simetri = taşı // Zaten geometri seçiliyse klavye beklemeden doğrudan aktif (faz 1'e geç). if (!sketchSecili.empty() && sketchDuzenIlerlet) sketchDuzenIlerlet(); }; const auto sketchDurumMetin = [&](eskiz::EskizVeri& ev) { switch (ev.durum()) { case eskiz::Solver::Durum::IyiKisitli: bilgiVer("Sketch tam belirli"); break; case eskiz::Solver::Durum::AzKisitli: bilgiVer("Az belirli — " + std::to_string(ev.dof()) + " DOF"); break; case eskiz::Solver::Durum::AsiriKisitli: bilgiVer("Aşırı belirli (fazla kısıt)"); break; case eskiz::Solver::Durum::Celiskili: bilgiVer("Çelişkili kısıt"); break; case eskiz::Solver::Durum::Yakinsamadi: bilgiVer("Çözücü yakınsamadı"); break; } }; // Seçili geometrilere kısıt uygula. deger<0 → boyutsuz kısıt; boyutsal // kısıtlar (Mesafe/Yarıçap) mevcut ölçüyü hedef alır (deger). const auto sketchKisitUygula = [&, sketchNesne, sketchDurumMetin](eskiz::KisitTip tip) { SketchNesnesi* sk = sketchNesne(); if (!sk) return; eskiz::EskizVeri& ev = sk->eskiz(); if (sketchSecili.empty()) { bilgiVer("Önce eskiz geometrisi seçin"); return; } if (tip == eskiz::KisitTip::Midpoint) { // Orta Nokta: BİR nokta + BİR çizgi (seçim sırası serbest — normalize edilir; // solver sözleşmesi geo[0]=nokta, geo[1]=çizgi ister). const eskiz::Geometri* g0 = sketchSecili.size() == 2 ? ev.geoBul(sketchSecili[0]) : nullptr; const eskiz::Geometri* g1 = sketchSecili.size() == 2 ? ev.geoBul(sketchSecili[1]) : nullptr; const eskiz::Geometri* nokta = g0 && g0->tip == eskiz::GeoTip::Nokta ? g0 : (g1 && g1->tip == eskiz::GeoTip::Nokta ? g1 : nullptr); const eskiz::Geometri* cizgi = g0 && g0->tip == eskiz::GeoTip::Cizgi ? g0 : (g1 && g1->tip == eskiz::GeoTip::Cizgi ? g1 : nullptr); if (!nokta || !cizgi) { bilgiVer("Orta Nokta: bir NOKTA ve bir ÇİZGİ seçin"); return; } ev.kisitEkle(eskiz::KisitTip::Midpoint, {nokta->kimlik, cizgi->kimlik}, {0, 0}, 0); ev.coz(); sketchSecili.clear(); sketchDurumMetin(ev); return; } eskiz::Skalar deger = 0; if (tip == eskiz::KisitTip::Mesafe && !sketchSecili.empty()) { const eskiz::Geometri* g = ev.geoBul(sketchSecili[0]); if (g && g->tip == eskiz::GeoTip::Cizgi) { const eskiz::Skalar dx = ev.noktaX(g->kimlik, 1) - ev.noktaX(g->kimlik, 0); const eskiz::Skalar dy = ev.noktaY(g->kimlik, 1) - ev.noktaY(g->kimlik, 0); deger = std::sqrt(dx * dx + dy * dy); } } else if (tip == eskiz::KisitTip::Yaricap && !sketchSecili.empty()) { const eskiz::Geometri* g = ev.geoBul(sketchSecili[0]); if (g && (g->tip == eskiz::GeoTip::Daire || g->tip == eskiz::GeoTip::Yay)) deger = ev.px(g->ir); } ev.kisitEkle(tip, sketchSecili, deger); ev.coz(); sketchSecili.clear(); sketchDurumMetin(ev); }; const auto sketchAktifMi = [&]() { return aktifSketch != VARLIK_YOK; }; // ---- 3B → Sketch geçirme (CATIA Project/Intersect 3D Elements MVP) ---- // İzdüşüm: SEÇİLİ 3B kenarlar sketch düzlemine (doğru→çizgi, paralel çember→daire, // diğerleri→spline — kenar3DSketchGecir). Arakesit: GÖRÜNÜR brep gövdeler sketch // düzlemiyle kesilir (kesit kalıbı, seçim gerekmez). Assosiyatif değil (statik). // Yapı Modu açıkken construction geometri üretilir (çizim araçlarıyla tutarlı). const auto sketch3DGecir = [&, sketchNesne, sketchDurumMetin](bool arakesit) { SketchNesnesi* sk = sketchNesne(); if (!sk) return; eskiz::EskizVeri& ev = sk->eskiz(); const Dugum* dz = sahne.bul(sk->duzlem()); if (!dz) { bilgiVer("Sketch düzlemi bulunamadı"); return; } int n = 0; if (arakesit) { for (Dugum& d : sahne.dugumler()) { if (!d.brep || !d.brep->gecerliMi() || !sahne.agacGorunur(d.kimlik)) continue; n += (int)arakesit3DSketchGecir(ev, *d.brep, d.donusum, dz->donusum, sketchYapiMod).size(); } if (!n) { bilgiVer("3B Arakesit: sketch düzlemi hiçbir gövdeyi kesmiyor"); return; } } else { for (const SecimOge& o : sahne.secililer()) { if (!o.alt) continue; Dugum* d = sahne.bul(o.govde); if (!d || !d->brep) continue; if (kenar3DSketchGecir(ev, *d->brep, o.alt, d->donusum, dz->donusum, sketchYapiMod)) ++n; } if (!n) { bilgiVer("3B İzdüşüm: önce kenar kipinde 3B kenar(lar) seçin (sketch'e girmeden)"); return; } } ev.coz(); sketchDurumMetin(ev); bilgiVer(std::to_string(n) + (arakesit ? " arakesit eğrisi" : " kenar") + " sketch'e geçirildi"); }; // Tek geometriye uygun boyutsal kısıtı mevcut ölçüyle ekle: çizgi → Mesafe // (uzunluk), daire/yay → Yarıçap. Dönüş: kısıt kimliği (uygulanamazsa 0). // sketchOtoOlcu + sketchOtoKisit ortak kullanır (mükerrer değil). const auto sketchGeoOlcu = [](eskiz::EskizVeri& ev, const eskiz::Geometri* g) -> u32 { if (g->tip == eskiz::GeoTip::Cizgi) return ev.kisitEkle(eskiz::KisitTip::Mesafe, {g->kimlik}, std::hypot(ev.noktaX(g->kimlik, 1) - ev.noktaX(g->kimlik, 0), ev.noktaY(g->kimlik, 1) - ev.noktaY(g->kimlik, 0))); if (g->tip == eskiz::GeoTip::Daire || g->tip == eskiz::GeoTip::Yay) return ev.kisitEkle(eskiz::KisitTip::Yaricap, {g->kimlik}, ev.px(g->ir)); return 0; }; // Otomatik ölçülendirme: seçime göre uygun boyutsal kısıtı mevcut ölçüyle ekle. // 1 çizgi → Mesafe(uzunluk), 1 daire/yay → Yarıçap, 2 nokta → Mesafe, // 2 çizgi → Açı. Ölçü hedefi geometrinin güncel değeridir (referans alır). const auto sketchOtoOlcu = [&, sketchNesne, sketchDurumMetin, sketchGeoOlcu]() { SketchNesnesi* sk = sketchNesne(); if (!sk) return; eskiz::EskizVeri& ev = sk->eskiz(); if (sketchSecili.empty()) { bilgiVer("Önce eskiz geometrisi seçin"); return; } if (sketchSecili.size() == 1) { const eskiz::Geometri* g = ev.geoBul(sketchSecili[0]); if (!g) return; if (!sketchGeoOlcu(ev, g)) { bilgiVer("Bu geometri için otomatik ölçü yok"); return; } } else if (sketchSecili.size() == 2) { const eskiz::Geometri* g0 = ev.geoBul(sketchSecili[0]); const eskiz::Geometri* g1 = ev.geoBul(sketchSecili[1]); if (!g0 || !g1) return; if (g0->tip == eskiz::GeoTip::Nokta && g1->tip == eskiz::GeoTip::Nokta) { const eskiz::Skalar dm = std::hypot(ev.noktaX(g1->kimlik, 0) - ev.noktaX(g0->kimlik, 0), ev.noktaY(g1->kimlik, 0) - ev.noktaY(g0->kimlik, 0)); ev.kisitEkle(eskiz::KisitTip::Mesafe, {g0->kimlik, g1->kimlik}, dm); } else if (g0->tip == eskiz::GeoTip::Cizgi && g1->tip == eskiz::GeoTip::Cizgi) { const eskiz::Skalar a0 = std::atan2(ev.noktaY(g0->kimlik, 1) - ev.noktaY(g0->kimlik, 0), ev.noktaX(g0->kimlik, 1) - ev.noktaX(g0->kimlik, 0)); const eskiz::Skalar a1 = std::atan2(ev.noktaY(g1->kimlik, 1) - ev.noktaY(g1->kimlik, 0), ev.noktaX(g1->kimlik, 1) - ev.noktaX(g1->kimlik, 0)); ev.kisitEkle(eskiz::KisitTip::Aci, {g0->kimlik, g1->kimlik}, std::abs(a1 - a0)); } else { bilgiVer("Bu seçim için otomatik ölçü yok"); return; } } else { bilgiVer("Otomatik ölçü: 1 veya 2 öğe seçin"); return; } ev.coz(); sketchSecili.clear(); sketchDurumMetin(ev); }; // Temas kısıtı (CATIA Contact Constraint): 2 seçili geometrinin tip çiftine // göre EN UYGUN kısıtı otomatik uygula — daire+daire → eş merkez (Cakisik // merkez-merkez), daire+çizgi → Teğet, nokta+nokta → Çakışık, nokta+geo → // NoktaUzerinde, çizgi+çizgi → Çakışık (en yakın uç çifti). const auto sketchTemasKisit = [&, sketchNesne, sketchDurumMetin]() { SketchNesnesi* sk = sketchNesne(); if (!sk) return; eskiz::EskizVeri& ev = sk->eskiz(); if (sketchSecili.size() != 2) { bilgiVer("Temas kısıtı: 2 öğe seçin"); return; } const eskiz::Geometri* g0 = ev.geoBul(sketchSecili[0]); const eskiz::Geometri* g1 = ev.geoBul(sketchSecili[1]); if (!g0 || !g1) return; using GT = eskiz::GeoTip; const auto daireMi = [](const eskiz::Geometri* g) { return g->tip == GT::Daire || g->tip == GT::Yay; }; const char* ad = nullptr; if (daireMi(g0) && daireMi(g1)) { ev.kisitEkle(eskiz::KisitTip::Cakisik, {g0->kimlik, g1->kimlik}, {2, 2}, 0); ad = "Eş merkez"; } else if ((g0->tip == GT::Cizgi && daireMi(g1)) || (daireMi(g0) && g1->tip == GT::Cizgi)) { // Solver Tegel sırası: g0=çizgi, g1=daire/yay. const u32 cz = g0->tip == GT::Cizgi ? g0->kimlik : g1->kimlik; const u32 dr = g0->tip == GT::Cizgi ? g1->kimlik : g0->kimlik; ev.kisitEkle(eskiz::KisitTip::Tegel, {cz, dr}); ad = "Teğet"; } else if (g0->tip == GT::Nokta || g1->tip == GT::Nokta) { const eskiz::Geometri* nk = g0->tip == GT::Nokta ? g0 : g1; const eskiz::Geometri* hd = g0->tip == GT::Nokta ? g1 : g0; if (hd->tip == GT::Nokta) { ev.kisitEkle(eskiz::KisitTip::Cakisik, {nk->kimlik, hd->kimlik}, {0, 0}, 0); ad = "Çakışık"; } else if (hd->tip == GT::Cizgi || daireMi(hd)) { ev.kisitEkle(eskiz::KisitTip::NoktaUzerinde, {nk->kimlik, hd->kimlik}, {0, 0}, 0); ad = "Nokta üzerinde"; } } else if (g0->tip == GT::Cizgi && g1->tip == GT::Cizgi) { // En yakın uç çiftini bul → o uçlar çakışık. int e0 = 0, e1 = 0; f64 enM = 1e300; for (int i = 0; i < 2; ++i) for (int j = 0; j < 2; ++j) { const f64 dx = ev.noktaX(g1->kimlik, j) - ev.noktaX(g0->kimlik, i); const f64 dy = ev.noktaY(g1->kimlik, j) - ev.noktaY(g0->kimlik, i); if (dx * dx + dy * dy < enM) { enM = dx * dx + dy * dy; e0 = i; e1 = j; } } ev.kisitEkle(eskiz::KisitTip::Cakisik, {g0->kimlik, g1->kimlik}, {e0, e1}, 0); ad = "Çakışık (uç-uç)"; } if (!ad) { bilgiVer("Bu geometri çifti için temas kısıtı yok"); return; } ev.coz(); sketchSecili.clear(); sketchDurumMetin(ev); }; // Oto Kısıt (CATIA Auto Constraint): seçili (boşsa tüm) geometrilere otomatik // kısıt seti — eğimi <5° çizgiye Yatay, >85° Dikey; her çizgiye Mesafe, daire/ // yaya Yarıçap (sketchGeoOlcu). Zaten o tipte kısıtı olan geometri atlanır; // ekleme sonrası çözüm Çelişkili/Aşırı olursa son eklenen geri alınır. const auto sketchOtoKisit = [&, sketchNesne, sketchDurumMetin, sketchGeoOlcu]() { SketchNesnesi* sk = sketchNesne(); if (!sk) return; eskiz::EskizVeri& ev = sk->eskiz(); std::vector hedef = sketchSecili; if (hedef.empty()) for (const eskiz::Geometri& g : ev.geometriler()) hedef.push_back(g.kimlik); if (hedef.empty()) { bilgiVer("Eskizde geometri yok"); return; } const auto kisitVar = [&ev](eskiz::KisitTip tip, u32 geoId) { for (const eskiz::Kisit& k : ev.kisitlar()) if (k.tip == tip) for (u32 g : k.geo) if (g == geoId) return true; return false; }; // Kısıtı geçici uygula; çözüm bozulursa (Çelişkili/Aşırı) geri al. const auto guvenliEkle = [&ev](u32 kimlik) -> bool { if (!kimlik) return false; const auto d = ev.coz().durum; if (d == eskiz::Solver::Durum::Celiskili || d == eskiz::Solver::Durum::AsiriKisitli) { ev.kisitSil(kimlik); ev.coz(); return false; } return true; }; int eklendi = 0; for (u32 id : hedef) { const eskiz::Geometri* g = ev.geoBul(id); if (!g) continue; if (g->tip == eskiz::GeoTip::Cizgi) { const f64 dx = std::abs(ev.noktaX(id, 1) - ev.noktaX(id, 0)); const f64 dy = std::abs(ev.noktaY(id, 1) - ev.noktaY(id, 0)); const f64 t5 = 0.0874886; // tan(5°): eğim eşiği if (dy < dx * t5 && !kisitVar(eskiz::KisitTip::Yatay, id) && !kisitVar(eskiz::KisitTip::Dikey, id)) eklendi += guvenliEkle(ev.kisitEkle(eskiz::KisitTip::Yatay, {id})); else if (dx < dy * t5 && !kisitVar(eskiz::KisitTip::Dikey, id) && !kisitVar(eskiz::KisitTip::Yatay, id)) eklendi += guvenliEkle(ev.kisitEkle(eskiz::KisitTip::Dikey, {id})); if (!kisitVar(eskiz::KisitTip::Mesafe, id)) eklendi += guvenliEkle(sketchGeoOlcu(ev, g)); } else if (g->tip == eskiz::GeoTip::Daire || g->tip == eskiz::GeoTip::Yay) { if (!kisitVar(eskiz::KisitTip::Yaricap, id) && !kisitVar(eskiz::KisitTip::Cap, id)) eklendi += guvenliEkle(sketchGeoOlcu(ev, g)); } } sketchSecili.clear(); if (!eklendi) { bilgiVer("Oto kısıt: eklenecek yeni kısıt yok"); return; } ev.coz(); sketchDurumMetin(ev); }; // ---- Sketch mantık delegeleri bağla (sketch_ui.cpp modülü main'in yerel // lambda'larına dokunmadan bunları çağırır — davranış birebir). ---- bglm.sketchNesne = sketchNesne; // yerel lambda'yı KOPYALA (sarma DEĞİL) — kur() dönünce // yerel lambda ölür; kopya kalıcı referentleri (aktifSketch/cekirdek) taşır // Wrapper DEĞİL, KOPYA: yerel lambda'lar kendine-yeter (nested yerel çağrılarını // by-value yakalar) → kur() dönünce bglm kopyası dangling içermez. (1384 satırındaki // sketchAracTamamla ataması ile tekilleştirildi — mükerrer/ezici wrapper kaldırıldı.) bglm.sketchDuzenMi = sketchDuzenMi; bglm.sketchDikBak = sketchDikBak; bglm.sketchAracSec = sketchAracSec; bglm.sketchDuzenSec = sketchDuzenSec; bglm.sketchDuzenIlerlet = sketchDuzenIlerlet; bglm.sketchKisitUygula = sketchKisitUygula; bglm.sketchOtoOlcu = sketchOtoOlcu; bglm.sketch3DGecir = sketch3DGecir; bglm.sketchTemasKisit = sketchTemasKisit; bglm.sketchOtoKisit = sketchOtoKisit; // NOT: kur()-yerel sketchAracSec/sketchDuzenSec dangling → KALICI bglm kopyası yakala // (satır 1519-1520). Menü lambda'ları kur() sonrası kareCiz'de çağrılır (Kısıt deseni). // Alt şerit KATEGORİLİ/FLYOUT: aynı altGrup → tek küme düğmesi (▾), tıkla açıl. // 32 düğme yerine ~10 kategori (CATIA Profile/Operation/Constraint toolbar mantığı). // Çizim: Çizgi | [Dikdörtgen▾] | [Daire▾] | [Eğri▾] | Nokta. komut("Çizim", "Çizgi", "╱", [&bglm](Cekirdek&) { bglm.sketchAracSec("cizgi"); }, "sketcher", etkinSketch, "cizgi"); komut("Çizim", "Sonsuz Çizgi", "∞", [&bglm](Cekirdek&) { bglm.sketchAracSec("sonsuzCizgi"); }, "sketcher", etkinSketch, "cizgi"); komut("Çizim", "Açıortay", "∠", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("aciortay"); }, "sketcher", etkinSketch, "cizgi"); komut("Çizim", "İki-Teğet Çizgi", "⌣", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("tegetcizgi"); }, "sketcher", etkinSketch, "cizgi"); komut("Çizim", "Dikdörtgen", "▭", [&bglm](Cekirdek&) { bglm.sketchAracSec("dikdortgen"); }, "sketcher", etkinSketch, "dikdortgen"); komut("Çizim", "Merkezli Dikdörtgen", "▢", [&bglm](Cekirdek&) { bglm.sketchAracSec("merkezliDikdortgen"); }, "sketcher", etkinSketch, "dikdortgen"); komut("Çizim", "Yönlü Dikdörtgen", "◇", [&bglm](Cekirdek&) { bglm.sketchAracSec("yonluDikdortgen"); }, "sketcher", etkinSketch, "dikdortgen"); komut("Çizim", "Paralelkenar", "▱", [&bglm](Cekirdek&) { bglm.sketchAracSec("paralelkenar"); }, "sketcher", etkinSketch, "dikdortgen"); komut("Çizim", "Daire", "◯", [&bglm](Cekirdek&) { bglm.sketchAracSec("daire"); }, "sketcher", etkinSketch, "daire"); komut("Çizim", "Daire (3N)", "◎", [&bglm](Cekirdek&) { bglm.sketchAracSec("daire3"); }, "sketcher", etkinSketch, "daire"); komut("Çizim", "Daire (Koord)", "⌖", [&bglm](Cekirdek&) { bglm.sketchAracSec("daireKoord"); }, "sketcher", etkinSketch, "daire"); komut("Çizim", "Yay (3N)", "⌒", [&bglm](Cekirdek&) { bglm.sketchAracSec("yay3"); }, "sketcher", etkinSketch, "egri"); komut("Çizim", "Spline", "∿", [&bglm](Cekirdek&) { bglm.sketchAracSec("spline"); }, "sketcher", etkinSketch, "egri"); // NURBS Egri: tikla->kontrol noktasi ekle, cift-tik bitir. komut("Çizim", "NURBS Egri", "∿", [&bglm](Cekirdek&) { bglm.sketchAracSec("nurbsEgri"); }, "sketcher", etkinSketch, "egri"); komut("Çizim", "3B İzdüşüm", "⤓", [&bglm](Cekirdek&) { bglm.sketch3DGecir(false); }, "sketcher", etkinSketch, "egri"); komut("Çizim", "3B Arakesit", "⌓", [&bglm](Cekirdek&) { bglm.sketch3DGecir(true); }, "sketcher", etkinSketch, "egri"); komut("Çizim", "Merkez Yay", "◜", [&bglm](Cekirdek&) { bglm.sketchAracSec("merkezYay"); }, "sketcher", etkinSketch, "egri"); komut("Çizim", "Elips", "⬭", [&bglm](Cekirdek&) { bglm.sketchAracSec("elips"); }, "sketcher", etkinSketch, "egri"); komut("Çizim", "Eliptik Yay", "◜", [&bglm](Cekirdek&) { bglm.sketchAracSec("elipsYay"); }, "sketcher", etkinSketch, "egri"); komut("Çizim", "Slot", "▬", [&bglm](Cekirdek&) { bglm.sketchAracSec("slot"); }, "sketcher", etkinSketch, "egri"); komut("Çizim", "Eğrisel Slot", "◠", [&bglm](Cekirdek&) { bglm.sketchAracSec("egriselSlot"); }, "sketcher", etkinSketch, "egri"); komut("Çizim", "Anahtar Deliği", "⚿", [&bglm](Cekirdek&) { bglm.sketchAracSec("anahtarDeligi"); }, "sketcher", etkinSketch, "egri"); komut("Çizim", "Çokgen", "⬡", [&bglm](Cekirdek&) { bglm.sketchAracSec("cokgen"); }, "sketcher", etkinSketch, "egri"); komut("Çizim", "Nokta", "·", [&bglm](Cekirdek&) { bglm.sketchAracSec("nokta"); }, "sketcher", etkinSketch, "nokta"); komut("Çizim", "Nokta (Koord)", "⊹", [&bglm](Cekirdek&) { bglm.sketchAracSec("noktaKoord"); }, "sketcher", etkinSketch, "nokta"); komut("Çizim", "Kesişim Noktası", "✕", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("kesisimnokta"); }, "sketcher", etkinSketch, "nokta"); komut("Çizim", "İzdüşüm Noktası", "⊥", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("izdusumnokta"); }, "sketcher", etkinSketch, "nokta"); // ---- Düzenle (2B Operations): [Kırp▾] | [Köşe▾] | [Dönüşüm▾] ---- komut("Düzenle", "Hızlı Kırp (Silgi)", "✄", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("hizlikirp"); }, "sketcher", etkinSketch, "kirp"); komut("Düzenle", "Kırp", "✂", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("trim"); }, "sketcher", etkinSketch, "kirp"); komut("Düzenle", "Uzat", "⇥", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("uzat"); }, "sketcher", etkinSketch, "kirp"); komut("Düzenle", "Böl", "⑂", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("bol"); }, "sketcher", etkinSketch, "kirp"); komut("Düzenle", "Kapat", "◯", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("kapat"); }, "sketcher", etkinSketch, "kirp"); komut("Düzenle", "Tümleyen", "◟", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("tumleyen"); }, "sketcher", etkinSketch, "kirp"); komut("Düzenle", "Köşe", "⌒", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("fillet"); }, "sketcher", etkinSketch, "kose"); komut("Düzenle", "Pah", "◺", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("pah"); }, "sketcher", etkinSketch, "kose"); komut("Düzenle", "Ayna", "⇋", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("ayna"); }, "sketcher", etkinSketch, "donusum"); komut("Düzenle", "Simetri", "↔", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("simetri"); }, "sketcher", etkinSketch, "donusum"); komut("Düzenle", "Ötele", "⤢", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("otele"); }, "sketcher", etkinSketch, "donusum"); komut("Düzenle", "Döndür", "↻", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("dondur"); }, "sketcher", etkinSketch, "donusum"); komut("Düzenle", "Ölçekle", "⤡", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("olcekle"); }, "sketcher", etkinSketch, "donusum"); komut("Düzenle", "Ofset", "∥", [&bglm](Cekirdek&) { bglm.sketchDuzenSec("ofset"); }, "sketcher", etkinSketch, "donusum"); // ---- Kısıt: [Geometrik▾] | [Ölçüsel▾] ---- // KRİTİK: menü lambda'ları kur() döndükten sonra kareCiz'de çağrılır → yerel // (sketchKisitUygula/sketchAktifMi) [&] yakalamak dangling'dir (0xC0000005 hover // çökmesi). KALICI bglm kopyası + kararlı member aktifSketch yakala. const auto kisitEtkin = [&aktifSketch](Cekirdek&) { return aktifSketch != VARLIK_YOK; }; komut("Kısıt", "Yatay", "H", [&bglm](Cekirdek&) { bglm.sketchKisitUygula(eskiz::KisitTip::Yatay); }, "sketcher", kisitEtkin, "geokisit"); komut("Kısıt", "Dikey", "V", [&bglm](Cekirdek&) { bglm.sketchKisitUygula(eskiz::KisitTip::Dikey); }, "sketcher", kisitEtkin, "geokisit"); komut("Kısıt", "Dik", "⊥", [&bglm](Cekirdek&) { bglm.sketchKisitUygula(eskiz::KisitTip::Dik); }, "sketcher", kisitEtkin, "geokisit"); komut("Kısıt", "Paralel", "∥", [&bglm](Cekirdek&) { bglm.sketchKisitUygula(eskiz::KisitTip::Paralel); }, "sketcher", kisitEtkin, "geokisit"); komut("Kısıt", "Eşit", "=", [&bglm](Cekirdek&) { bglm.sketchKisitUygula(eskiz::KisitTip::Esit); }, "sketcher", kisitEtkin, "geokisit"); komut("Kısıt", "Çakışık", "∘", [&bglm](Cekirdek&) { bglm.sketchKisitUygula(eskiz::KisitTip::Cakisik); }, "sketcher", kisitEtkin, "geokisit"); komut("Kısıt", "Temas", "◉", [&bglm](Cekirdek&) { bglm.sketchTemasKisit(); }, "sketcher", kisitEtkin, "geokisit"); komut("Kısıt", "Orta Nokta", "⋈", [&bglm](Cekirdek&) { bglm.sketchKisitUygula(eskiz::KisitTip::Midpoint); }, "sketcher", kisitEtkin, "geokisit"); komut("Kısıt", "Mesafe", "↦", [&bglm](Cekirdek&) { bglm.sketchKisitUygula(eskiz::KisitTip::Mesafe); }, "sketcher", kisitEtkin, "olcukisit"); komut("Kısıt", "Yarıçap", "⌀", [&bglm](Cekirdek&) { bglm.sketchKisitUygula(eskiz::KisitTip::Yaricap); }, "sketcher", kisitEtkin, "olcukisit"); komut("Kısıt", "Oto Ölçü", "⊢", [&bglm](Cekirdek&) { bglm.sketchOtoOlcu(); }, "sketcher", kisitEtkin, "olcukisit"); komut("Kısıt", "Oto Kısıt", "⚙", [&bglm](Cekirdek&) { bglm.sketchOtoKisit(); }, "sketcher", kisitEtkin, "olcukisit"); komut("Kısıt", "Animasyon", "▶", [&bglm](Cekirdek&) { sketchAnimasyonAc(bglm); }, "sketcher", kisitEtkin, "olcukisit"); // Fare girdisi ImGui IO uzerinden 3D pencere icinde islenir (capture ImGui'de); // boylece 3D pencere bir ImGui penceresi olsa bile kamera/secim calisir. // Boyut değişimi BU pencerenin KENDİ yüzeyini işaretler (aktif yüzey değil — resize // callback'i mesaj pompasında, aktif yüzey son çizilen pencereninki olabilir). pencere.boyutDegisti = [this](u32, u32) { if (anaMi_) vulkan_.anaYuzeyNesne().boyutlandirmaGerekli(); else if (yuzeySahip_) yuzeySahip_->boyutlandirmaGerekli(); }; // Modal resize/move döngüsü (WM_ENTERSIZEMOVE) içinden bir kare çiz → ekran donmaz, // GPU fence beslenir (timeout önlenir). Re-entrancy guard: kareCalistir'in içindeki // olası mesaj işleme WM_TIMER'ı yeniden tetiklerse tekrar girme. pencere.kareCiz = [this] { if (kareCizIcinde_) return; kareCizIcinde_ = true; kareCalistir(); kareCizIcinde_ = false; }; // Explorer'dan surukle-birak: birakilan her dosyayi bu belgeye ac/ice aktar. // yolAc uzantiya gore dogru iceaktarici secer (.step/.stp→STEP, .apt/.FikoApt→APT); // desteklenmeyen/erisilemez yolda kendi guard'i var (cokme yok). // Explorer sürükle-bırak: komut soyutlamasından GEÇMEZ (kullanıcı menüye hiç // dokunmaz) → içe aktarma hakkı burada AYRICA sorulur. Uzantıya göre hangi komut // hakkının gerektiği belirlenir; hakkı yoksa dosya açılmaz + neden bildirilir. pencere.dosyaBirakildi = [&bglm](const std::string& yol) { if (!surukleBirakIzinli(yol)) { ui::toastGoster("Bu dosya türünü açma hakkı lisansınıza dahil değil", ui::ToastTip::Uyari); return; } fiko::yolAc(bglm, yol); }; // ---- Dokunmatik / kalem (WM_POINTER) ---- // TEMEL KURAL: yalnız 3B viewport'taki NAV jestleri tüketilir. Tüketmezsek Windows // dokunuşu fare mesajına yükseltir → ImGui panelleri/menüler/ağaç parmakla aynen // çalışır ve fare yolu (nav_kip CATIA şeması) hiç değişmez. // Kalem + "İşaretçi" eylemi de BİLEREK tüketilmez: fare yükseltmesi kalemin ucunu // sol-tuşa çevirir → seçim/trap kutusu/sketch çizimi/hover mevcut kodla birebir, // hassasiyet kaybı yok (kalem tek işaretçidir, çok-parmak belirsizliği olmaz). pencere.dokunmaOlayi = [this](const DokunmaOlay& o) -> bool { if (!dokunmatikAktif()) return false; auto& d = ort_.dokunma; if (o.tur == DokunmaOlayTur::Iptal) { d.iptal(); dokunmaParmakAktif() = false; return false; } if (o.tur == DokunmaOlayTur::Bas) { const bool vpIcinde = o.x >= ort_.vpRectMinX && o.y >= ort_.vpRectMinY && o.x < ort_.vpRectMinX + ort_.sonG && o.y < ort_.vpRectMinY + ort_.sonY_; if (!vpIcinde) return false; if (o.kalem) { const girdi::DokunmaJest kj = o.govdeTusu ? girdi::DokunmaJest::KalemGovdeSurukle : girdi::DokunmaJest::KalemUcSurukle; if (dokunmaEslemeGlobal().al(kj) == girdi::DokunmaEylem::Isaretci) return false; } d.basildi(o.kimlik, {o.x, o.y}, o.kalem ? girdi::IsaretciTur::Kalem : girdi::IsaretciTur::Dokunma, o.govdeTusu); // Gizmo hit-test'i büyüt: yalnız PARMAK (kalem ucu hassas, büyütmek yanlış // tutamacı yakalatır). Bkz. dokunmaHedefCarpani() — dogrudan_ayar.hpp. if (!o.kalem) dokunmaParmakAktif() = true; return true; } // BAS'ta almadıysak (panel üstü / işaretçi kalemi) HAREKET/BIRAK da Windows'un. if (!d.tanirMi(o.kimlik)) return false; if (o.tur == DokunmaOlayTur::Hareket) d.hareket(o.kimlik, {o.x, o.y}, o.govdeTusu); else { d.birakildi(o.kimlik); // Son parmak kalktı → hit-test fare hassasiyetine dönsün (fare kullanıcısı // aynı makinede dokunmatiğe dokunduktan sonra kaba hedefle kalmasın). if (d.aktifSayi() == 0) dokunmaParmakAktif() = false; } return true; }; // ---- BASİT/GLOBAL kısayolları merkezi kayda ekle (mod-bağımsız, kendine-yeten // eylem + tuş tüketir). Bağlamsal kısayollar (1-4/F/T/O/G/M/Space/Delete/ESC...) // tusBasildi if-zincirinde KALIR — mod-bağımlı, taşımak riskli (davranış birebir). // Yakalama [&]: eski tusBasildi lambda'sıyla aynı semantik (belge/üye referansı, // pencere ömrü boyunca geçerli). kisayollar_ üye → kur() sonrası da yaşar. kisayollar_.ekle({'Z', true, false, false, "Geri Al", [&] { cekirdek.geriAl(); }}); kisayollar_.ekle({'Y', true, false, false, "Yinele", [&] { cekirdek.yinele(); }}); // Ctrl+S = .fikocad kaydet, Ctrl+O = .fikocad aç. (Modsuz 'O' ortografik toggle // olarak kalır — kayıt ctrl=true, çakışmaz.) kisayollar_.ekle({'S', true, false, false, "Kaydet (.fikocad)", [&] { fiko::fikoKaydetKomut(bglm); }}); kisayollar_.ekle({'O', true, false, false, "Aç (.fikocad)", [&] { fiko::fikoAc(bglm); }}); // Merkezi takip: kısayol çalışınca KomutCalisti yayınla → kısayollar da tek audit/ // telemetri akışına girer (menü/palet/toolbar ile aynı; lisans için tek-merkez). kisayollar_.kisayolCalisti = [&](const std::string& ad) { cekirdek.olaylar().yayinla(KomutCalisti{ad.c_str(), "Kısayol", "kisayol", true}, ad.c_str()); }; pencere.tusBasildi = [&](u32 vk) { const ImGuiIO& io = ImGui::GetIO(); // Metin kutusuna yazılıyorsa (ImGui InputText aktif) kısayolları TETİKLEME — tuş // ImGui'ye gitsin. Yoksa Ctrl+Z metin-geri-al yerine global "Geri Al"ı, 'F'/'O' vb. // yazı yerine komut tetikler (CATIA/standart editör davranışı). if (io.WantTextInput) return; // Merkezi global kısayollar ÖNCE: eşleşirse eylem çalışır + tuş tüketilir. // shift/alt=false geçilir → eski gevşek "io.KeyCtrl" (shift/alt önemsiz) // semantiği BİREBİR korunur (Ctrl+Z/Ctrl+Y kayıtları ctrl=true,shift/alt=false). if (kisayollar_.bas(vk, io.KeyCtrl, false, false)) return; if (vk == '1') secimSis.fYuz = !secimSis.fYuz; // seçim filtresi aç/kapa if (vk == '2') secimSis.fKenar = !secimSis.fKenar; if (vk == '3') secimSis.fNokta = !secimSis.fNokta; if (vk == '4') secimSis.fGovde = !secimSis.fGovde; if (vk == 'F') { if (io.KeyShift) tumunuSigdir(); else secimiSigdir(); } if (vk == 'T') { // alan seçimi trap tipini döngüsel değiştir (CATIA dışlayıcı) alan.tip = sonrakiTrap(alan.tip); bilgiVer(std::string("Alan seçimi: ") + trapAdi(alan.tip)); } if (vk == 'O') kamera.ortografikAyarla(!kamera.ortografik()); // ortografik/perspektif if (vk == 'G') { // compass'ı seçili gövdeye yapıştır / bırak if (pusula.bagli) { pusula.bagli = false; pusula.tutamac = -1; } else { const auto sg = sahne.seciliGovdeler(); if (!sg.empty()) { pusula.bagli = true; pusula.govde = sg.back(); pusula.tutamac = -1; } } } if (vk == 'M') { olcum.acik = !olcum.acik; if (olcum.acik && olcum.kip == OlcumKip::Ayarlar) olcum.kip = OlcumKip::Cetvel; } if (vk == VK_SPACE) { // Ctrl+Space kontrol merkezini açar — ama bu, ImGui kare-döngüsündeki tek // yetkili tetikleyicide (komutPalet) ele alınır; burada YALNIZ düz Space. // Böylece eski/yeni iki paletin aynı anda açılması (çift kutu) engellenir. if (!io.KeyCtrl) hizliGorunumAcik = true; } if (vk == VK_DELETE) { // Ağaç KLAVYE odağındaysa Delete'i ÜRÜN AĞACI işler (dugumSil → feature/grup/sketch/ // parametre/ölçüm TÜM tipler + çoklu). Global gövde-silme (seciliSil) ATLANIR; aksi halde // ağaçtan feature/grup silmeye çalışınca yanlız gövde silinip düğüm silinmiyordu. if (agacKlavyeOdakta()) return; // K7: Sketcher aktifken Delete gövde SİLMEZ — seçili sketch geometrisini // siler (yoksa no-op + bilgi). Aksi halde sahne gövdesi silme akışı. // kur()-yerel sketchNesne dangling → KALICI bglm kopyası (satır 1512). if (SketchNesnesi* sk = bglm.sketchNesne(); sk && cekirdek.workbenchler().aktif() == "sketcher") { if (!sketchSecili.empty() || !sketchSeciliKisit.empty()) { for (u32 k : sketchSeciliKisit) sk->eskiz().kisitSil(k); // seçili kısıt sembolleri for (u32 g : sketchSecili) sk->eskiz().geoSil(g); // seçili geometri (+ bağlı kısıt) sketchSeciliKisit.clear(); sketchSecili.clear(); sk->eskiz().coz(); // kısıt kalkınca DOF/çözüm tazele cekirdek.olaylar().yayinla(ParametreDegisti{aktifSketch}, "Sketch"); } else { bilgiVer("Silmek için önce sketch geometrisi/kısıt seçin"); } } else if (dogrudanAyar().silYuzKaldir && cekirdek.workbenchler().aktif() == "dogrudan" && sahne.kip() == SecimKip::Yuz && std::any_of(sahne.secililer().begin(), sahne.secililer().end(), [](const SecimOge& o) { return o.alt > 0; })) { // DM'de yüz seçiliyken Delete = Yüz Kaldır (Remove Face + heal) — gövde silme değil. // RE-EDITABLE FEATURE: her gövdeye ayrı remove_face feature'ı (ürün ağacına düğüm, // parametrik — DuzenleIslem tek-atış yerine; menü "Yüz Kaldır" ile aynı yol). std::vector>> gruplar; for (const SecimOge& o : sahne.secililer()) { if (!o.alt) continue; auto it = std::find_if(gruplar.begin(), gruplar.end(), [&](const auto& gr) { return gr.first == o.govde; }); if (it == gruplar.end()) gruplar.push_back({o.govde, {o.alt}}); else it->second.push_back(o.alt); } int tamam = 0, hata = 0; for (const auto& [gg, yuzler] : gruplar) { const u32 fk = partD.featureBaslat("remove_face", VARLIK_YOK, gg); if (!fk) { ++hata; continue; } std::vector> params{{"yuzSayi", (f64)yuzler.size()}}; for (size_t i = 0; i < yuzler.size(); ++i) params.emplace_back("yuz" + std::to_string(i), (f64)yuzler[i]); if (dogrudanAyar().asyncApply && partD.featureAsyncUygula(fk, params, [&cekirdek, &partD, fk](bool ok) { if (ok) dmFeatureEkleUndo(cekirdek, partD, fk); ui::toastGoster(ok ? "Yüz(ler) kaldırıldı" : "Yüz Kaldır başarısız (komşular kapanamadı)", ok ? ui::ToastTip::Basari : ui::ToastTip::Hata); })) { ++tamam; continue; } partD.featureParamToplu(fk, params); if (partD.featureOnayla(fk)) dmFeatureEkleUndo(cekirdek, partD, fk); ++tamam; } if (dogrudanAyar().asyncApply) { if (tamam) ui::toastGoster("Yüz kaldırılıyor…", ui::ToastTip::Bilgi); } else { if (tamam) ui::toastGoster("Yüz(ler) kaldırıldı", ui::ToastTip::Basari); if (hata) ui::toastGoster("Yüz Kaldır başarısız (komşular kapanamadı)", ui::ToastTip::Hata); } } else { seciliSil(); } } if (vk == VK_ESCAPE) { if (secimYigini.acikMi()) { // Selection Stack açık → seçime dokunmadan iptal secimYigini.kapat(); secimSis.hoverBastir(false); } else if (alan.aktifMi()) alan.iptal(); // trap sürükleme/poligon iptal else if (hizliYonSecim > 0) hizliYonSecim = 0; // "Yön Seç" modunu iptal (tık önceliğiyle aynı sıra) else if (refSecim.aktif()) refSecim.bitir(); else if (analizSis.secimAktif()) analizSis.secimBitir(); else if (camSis.secim().aktif()) camSis.secim().bitir(); // K3: Ölçüm modu ESC zincirine dahil — aktif nokta-koyma akışını iptal et, // hâlâ açıksa ölçüm penceresini kapat (diğer tek-tık modlarıyla tutarlı). else if (olcum.acik && olcum.kip != OlcumKip::Ayarlar) { if (olcum.canliVar || olcum.ref1.var) olcum.secimSifirla(); // aktif akışı iptal else olcum.acik = false; // boşsa modu kapat } else if (!sketchArac.empty()) { sketchArac.clear(); sketchNoktalar.clear(); sketchDuzenFaz = 0; sketchSecili.clear(); // düzenleme akışını da iptal et } else if (!sketchSecili.empty()) sketchSecili.clear(); else if (pusula.bagli) { pusula.bagli = false; pusula.tutamac = -1; } else sahne.secimTemizle(); } // Düzenleme modu faz ilerlet: Enter → aynı callback (ekran "Devam" ile ortak yol). // kur()-yerel sketchDuzenIlerlet dangling → KALICI bglm kopyası (satır 1521). if (vk == VK_RETURN && bglm.sketchDuzenIlerlet) bglm.sketchDuzenIlerlet(); }; // ---- g_araclar kurulum (komut registry dolduktan SONRA) ---- // Aday havuzu (özelleştirme "buton ekle") + "Geometri" grubundan başlangıç çubuğu. { g_araclar.menudenAdaylar(cekirdek); ui::AracCubugu gc; gc.kimlik = "geometri_cubugu"; gc.baslik = "Geometri"; gc.kenar = ui::DockKenar::Ust; gc.gorunur = false; // varsayılan kapalı (kullanıcı Görünüm'den açar) g_araclar.ekle(std::move(gc)); g_araclar.menudenDoldur(cekirdek, "geometri_cubugu", "Geometri"); g_araclar.workbenchDegisti(cekirdek.workbenchler().aktif()); } // ---- Alt Şerit (SeritCubugu) ayarı: registry'den HER KARE çizilir (kopyasız). // Grupları aktif workbench'e göre filtreler; komutları etkinMi ile pasifleştirir. { ui::SeritAyar sa; sa.grupSira = {"Düzenle", "Sketch", "Çizim", "Geometri", "Boolean", "Feature", "İşlem", "Tel-Kafes", "Yüzey", "Analiz", "Döküm", "CAM", "Kısıt", "Dosya"}; sa.gizliGruplar = {"Görünüm", "Ölçüm", "Makro"}; // menü çubuğunda kalır sa.grupEtiket = {{"Düzenle", "DÜZENLE"}, {"Geometri", "PRİMİTİF KATI"}, {"Boolean", "BOOLEAN"}, {"Feature", "FEATURE"}, {"İşlem", "PAH / YUVARLAT"}, {"Tel-Kafes", "TEL-KAFES"}, {"Yüzey", "YÜZEY"}, {"Sketch", "ESKİZ"}, {"Çizim", "ÇİZİM"}, {"Analiz", "ANALİZ"}, {"Döküm", "DÖKÜM"}, {"CAM", "CAM"}, {"Kısıt", "KISIT"}, {"Dosya", "DOSYA"}}; sa.sagCiz = [](Cekirdek&) { ImGui::AlignTextToFramePadding(); ImGui::TextDisabled("%.0f FPS", ImGui::GetIO().Framerate); }; // Bağlam vurgusu: imleç-altı kategori bit'i → butonCiz hedefMaske eşleşenleri parlatır. // &ort (üye; stabil) yakalanır → lambda her kare canlı belge motorundan okur. sa.baglamBiti = [ortP = &ort](Cekirdek&) -> unsigned { if (!ortP->belgePtr) return 0u; const auto kat = ortP->belgePtr->baglamMotoru.sonKarar().hedef.kategori; if (kat == baglam::HedefKategori::Bos) return 0u; return baglam::kategoriBit(kat); }; g_serit.ayar() = std::move(sa); } // Merkezi ayarlar: kayıt + kalıcı yükleme + değişim köprüsü (ayar_kalici + // AyarDegisti olayı) + "Araçlar → ⚙ Ayarlar…" menü komutu (ayar_kayitlari içinde). // ayarlarDurum SINIF ÜYESİ (persistence: settings command onu [&]=this yakalar). // Makrolar palete: mevcut makro dosyaları "Makro" grubunda komut — arama menüsü/ // paletten tek adımda çalışır (Makrolar menüsünü bulmak gerekmez). for (const std::string& yol : bglm.makro.makroListele(bglm.makroDizin)) makroKomutEkle(cekirdek, bglm.makro, yol); ayarlarKaydet(bglm, ayarlarDurum_); // Pencere konum/boyut kalıcılığı (Fikri 07-22 pencere standardı): ImGui ini exe // yanında; ayarlar yüklendikten SONRA (bayrak diskteki değerini almış olur) ve ilk // kareBasla'dan ÖNCE bağlanır. Ayar kapalı = eski davranış (hatırlama yok). // NoSavedSettings pencereleri (popup/menü/HUD) etkilenmez. if (pencereKonumHatirla()) arayuz.iniDosyasiAyarla(exeYaniYol("fikocad.pencere.ini")); // Oto-kayıt/çökme kurtarma: yalnız ANA belge (oturum dizini süreç-başına tek). if (anaMi_) otoKayit().bagla(bglm); // Son kullanılan dosyalar (MRU) — app-global, ayar'dan bir kez yükle (tüm pencereler // paylaşır; kur() MDI'de pencere-başına çalışır → tek yükleme guard'ı). if (!g_sonDosyalarYuklendi) { g_sonDosyalar.yukle(ayarOku("son_dosyalar", "")); g_sonDosyalarYuklendi = true; } // Kümülatif komut kullanımı (oturumlar arası telemetri) — app-global, bir kez yükle. if (!g_lisansYuklendi) { g_lisans.kullanimYukle(ayarOku("lisans_kullanim", "")); g_lisans.kotaYukle(ayarOku("lisans_kota", "")); g_lisansYuklendi = true; } // Orchestrator eşleştirme kayıtları (TOFU node kimlikleri) — app-global, bir kez yükle. if (!g_orkestraEslesmeYuklendi) { g_orkestraKule.eslesmeYukle(ayarOku("orkestra_eslesme", "")); g_orkestraEslesmeYuklendi = true; } // ---- Polimorfik panel kaydı (kayıt sırası = eski kareCiz çizim sırası, BİREBİR) ---- // Erken grup: Ürün Ağacı → Işık → Özellikler → Olay → Makro → Eklenti (~2966-2975). panelYon_.ekle(std::make_unique()); panelYon_.ekle(std::make_unique()); panelYon_.ekle(std::make_unique()); panelYon_.ekle(std::make_unique()); panelYon_.ekle(std::make_unique()); panelYon_.ekle(std::make_unique()); // App-global lisans/takip'i bu pencerenin Cekirdek'ine bağla (gate + KomutCalisti dinle). // Tüm pencereler tek g_lisans paylaşır → tutarlı lisans + toplu kullanım. g_lisans.bagla(cekirdek); // İmzalı lisans dosyasını BU NOKTADA yükle: komutlar kayıtlı (kimlikler hazır), ayarlar // uygulanmış. Lisans yoksa ya da geçersizse görüntüleyici kipi uygulanır. if (!g_lisansDosyaYuklendi) { lisansYukleVeUygula(g_lisans); g_lisansDosyaYuklendi = true; // UZAKTAN KOMUT KANALI: sunucudan gelen komutlari alir (Engelle/Kilitle/ // Lisans yenile/Komut yetkilendir). AYRI soket (/socket.io/) — mevcut // kill-switch kanali (/isans) degismeden calismaya devam eder. uzakKomutKanalBaslat(cekirdek); } // ---- Araç kipi: TEK araç ana pencere olarak (lisans/komut satırı/ortam) ---- // Lisans yüklendikten SONRA çözülür (kapı komut kimliklerine bakar). Kip açılırsa // ana kabuk (dockspace/ağaç/şerit/3D görünüm) çizilmez; yalnız o araç görünür. aracSistemi().bagla(cekirdek); if (aracOtomatikBaslat()) { // Tek pencere yanılsaması: OS pencere başlığı da aracın adı olsun ("FikoCAD // içinde bir araç" değil, "aracın kendisi"). Araç penceresi ayrıca çerçevesiz // olarak tüm istemci alanını doldurur (kafa_penceresi.cpp). if (const AracKayit* a = aracSistemi().aktif()) { const std::string ad = a->ad; const int n = MultiByteToWideChar(CP_UTF8, 0, ad.c_str(), (int)ad.size(), nullptr, 0); std::wstring genis(n > 0 ? (std::size_t)n : 0, L'\0'); if (n > 0) MultiByteToWideChar(CP_UTF8, 0, ad.c_str(), (int)ad.size(), genis.data(), n); SetWindowTextW((HWND)pencere_.dogalTutamac(), genis.c_str()); } // NOT: pencere boyutu BURADA değiştirilmez. SetWindowPos → WM_SIZE → swapchain // yeniden yaratma demek; açılışta (ilk kareler) bu, bilinen resize/WSI fence // yarışını tetikliyor ve süreç sessizce ölüyor (yaşandı). Dar boyut pencere // YARATILIRKEN verilir → pencereAyarKur. } // DM/PartDesign kapısı: gizmo sürüklemesi ve akıllı yeniden düzenleme komut // soyutlamasından GEÇMEZ; feature üreten tek nokta featureBaslat → oraya bağlanır. partD.duzenlemeKapisiAyarla([] { return lisansSistemi().duzenlemeIzinli(); }); // Lisans reddi SESSİZ kalmasın: kullanıcı düğmeye basıp "hiçbir şey olmadı" sanmasın. // Toast throttle'lı (aynı saniyede tek mesaj — sürükleme sırasında spam olmasın). cekirdek.olaylar().abone([](const LisansReddedildi&) { static f64 sonToast = -1.0; const f64 t = ImGui::GetTime(); if (t - sonToast < 1.5) return; sonToast = t; ui::toastGoster(lisansOturum().gecerliMi() ? "Bu komut lisansınıza dahil değil" : "Lisanssız — uygulama kilitli", ui::ToastTip::Uyari); }); // Güncelleme servisini bu pencerenin Cekirdek'ine bağla (olay erişimi + GorevSistemi async). g_guncelleme.bagla(cekirdek); // Dalga 1: açılışta sessiz oto-kontrol (ayar açıksa). Worker WinHTTP GET → main-thread // sürüm karşılaştır → yeni sürümde bildirim penceresi. Ağ yoksa sessizce Hata'da kalır. if (g_guncelleme.otoKontrol()) guncellemeKontrolBaslat(g_guncelleme, /*sessiz*/ true); // Komut Denetim / Lisans paneli (app-global g_lisans'ın görünür yönetimi) + toggle menü // komutu. Ham işaretçi panelYon_ (pencere ömrü) içinde yaşar → menü lambda güvenli. { auto lisansPanel = std::make_unique(g_lisans, cekirdek); FikoLisansPanel* lp = lisansPanel.get(); panelYon_.ekle(std::move(lisansPanel)); cekirdek.menuler().ekle({"Araçlar", "🔒 Komut Denetim", "🔒", 0, [lp](Cekirdek&) { lp->degistir(); }}); } // Lisans Üretici — YALNIZ ÜRETİCİ MAKİNEDE. Müşteride bu komut HİÇ kaydedilmez: // koşul özel imza anahtarının varlığı (o dosya yalnız bizde) veya FIKO_URETICI=1. // Kayıt edilmediği için menüde/palette/aramada da görünmez — gizleme değil, yokluk. #ifdef FIKO_URETICI_UI if (lisansUreticiModu()) { cekirdek.menuler().ekle({"Araçlar", "🏭 Lisans Üretici", "🏭", 0, [](Cekirdek&) { g_lisansUreticiAcik = !g_lisansUreticiAcik; }}); // Yönetim Konsolu: bağlı fikocad'ları listeler, anında lisans/komut verir. // Aynı üretici koşuluna bağlı — müşteride menü komutu HİÇ KAYDEDİLMEZ. cekirdek.menuler().ekle({"Araçlar", "🛠 Lisans Yönetimi", "🛠", 0, [](Cekirdek&) { g_lisansYonetimAcik = !g_lisansYonetimAcik; }}); cekirdek.menuler().ekle({"Araçlar", "🛰 Cihaz Merkezi", "🛰", 0, [](Cekirdek&) { g_cihazMerkeziAcik = !g_cihazMerkeziAcik; }}); } #endif // Merkezi süreç yöneticisi (görev yöneticisi dashboard) — IPC + olay akışı + sonlandırma. { auto surecPanel = std::make_unique(g_surecKopru, cekirdek); surecYonPanel_ = surecPanel.get(); // forward drain'i beslemek için ham işaretçi (pencere ömrü) FikoSurecYoneticiPanel* sp = surecYonPanel_; panelYon_.ekle(std::move(surecPanel)); cekirdek.menuler().ekle({"Araçlar", "🖧 Süreç Yöneticisi", "🖧", 0, [sp](Cekirdek&) { sp->degistir(); }}); } // Otomatik güncelleme: bildirim/denetim paneli + menü komutu (aç + hemen denetle). { auto gp = std::make_unique(g_guncelleme); FikoGuncellemePanel* gpp = gp.get(); panelYon_.ekle(std::move(gp)); cekirdek.menuler().ekle({"Araçlar", "🔄 Güncellemeleri Denetle", "🔄", 0, [gpp](Cekirdek&) { gpp->ac(); guncellemeKontrolBaslat(g_guncelleme, /*sessiz*/ false); }}); } // Geri bildirim: mesaj + otomatik 3B ekran görüntüsü → FikoYT sunucusu (kokDB ekle ucu). { auto gbp = std::make_unique(g_geriBildirim); FikoGeriBildirimPanel* gbpp = gbp.get(); panelYon_.ekle(std::move(gbp)); cekirdek.menuler().ekle({"Yardım", "💬 Geri Bildirim Gönder", "💬", 0, [gbpp](Cekirdek&) { gbpp->ac(); }}); } // Geri bildirim servisini bu pencerenin Cekirdek'ine bağla (GorevSistemi async POST). g_geriBildirim.cekirdekAyarla(&cekirdek); // Geç, bitişik batch: eski kareCiz ~3330-3344 çağrı sırası birebir korunur. // camUstte = ort.camUstte, dokumSis = belge.dokumSis, vulkan = vulkan_ (kur alias'ları). panelYonGec_.ekle(std::make_unique(camUstte)); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique(dokumSis)); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique(vulkan)); panelYonGec_.ekle(std::make_unique(hatlar_)); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique()); panelYonGec_.ekle(std::make_unique()); // İş Takip: merkezi teklif/sipariş/iş emri (Kral + MongoDB — docs/istakip_tasarim.md). cekirdek.menuler().ekle({"Araçlar", "📋 İş Takip", "📋", 0, [](Cekirdek&) { istakipPencereAc(); }}); cekirdek.menuler().ekle({"Araçlar", "⚙ Şema Tasarımcı", "⚙", 0, [](Cekirdek&) { istakipSemaPencereAc(); }}); // ---- CATIA Çalışma Alanı Kaydet/Geri Yükle (Faz 1 — bkz. docs/catia_calisma_alani.md) ---- // Ayrı sistem/UI yok: mevcut MenuKaydi registry'sine 2 komut. hedefMaske=Bos → boş // alanda sağ tık (bağlam radyal menü) + global menü/palet/makroda otomatik görünür. cekirdek.menuler().ekle( {"Araçlar", "💾 Çalışma Alanını Kaydet", "💾", 0, [](Cekirdek&) { fiko::ComOturum com; if (!com.basariliMi()) { ui::toastGoster("COM başlatılamadı — çalışma alanı kaydedilemedi", ui::ToastTip::Hata); return; } const auto kesif = fiko::catiaKesifYap(); const auto plan = fiko::planOlustur(kesif, fiko::windowsSurumTespitEt()); gunluk::bilgi("[calisma_alani] {}", fiko::catiaKesifRaporu(kesif)); std::string hata; const bool ok1 = fiko::calismaAlaniPlaniYaz(plan, fiko::sonPlanDosyaYolu(), hata); std::string hata2; const bool ok2 = ok1 && fiko::calismaAlaniPlaniYaz(plan, fiko::zamanDamgaliPlanDosyaYolu(), hata2); if (!ok1) { gunluk::hata("[calisma_alani] plan yazma basarisiz: {}", hata); ui::toastGoster(("Çalışma alanı kaydedilemedi: " + hata).c_str(), ui::ToastTip::Hata); } else if (kesif.oturumlar.empty()) { ui::toastGoster("Açık CATIA (CNEXT.exe) bulunamadı — boş plan kaydedildi", ui::ToastTip::Uyari); } else if (!ok2) { gunluk::hata("[calisma_alani] yedek kopya yazma basarisiz: {}", hata2); ui::toastGoster( (std::to_string(kesif.oturumlar.size()) + " CATIA oturumu kaydedildi (yedek kopya BAŞARISIZ — sadece ana dosya yazıldı)") .c_str(), ui::ToastTip::Uyari); } else { ui::toastGoster( (std::to_string(kesif.oturumlar.size()) + " CATIA oturumu kaydedildi").c_str(), ui::ToastTip::Basari); } }}); // Faz 2: artık salt-okunur günlük raporu değil, tam Çalışma Alanı Yöneticisi // panelini açar (kaydet/düzenle/seç/aç) — bkz. calisma_alani_penceresi.hpp // ve docs/catia_calisma_alani_arayuz_prompt.md. cekirdek.menuler().ekle({"Araçlar", "📂 Çalışma Alanını Geri Yükle", "📂", 0, [](Cekirdek&) { fiko::calismaAlaniPencereAc(); }}); if (MenuOge* o = cekirdek.menuler().bul("Araçlar", "💾 Çalışma Alanını Kaydet")) o->hedefMaske = baglam::maske(baglam::HedefKategori::Bos); if (MenuOge* o = cekirdek.menuler().bul("Araçlar", "📂 Çalışma Alanını Geri Yükle")) o->hedefMaske = baglam::maske(baglam::HedefKategori::Bos); // Dağınık paneller (İşlemler/pah3D blokları arasında) — yerinde tekil ciz() ile. hizli3dPanel_ = std::make_unique(); ayarlarPanel_ = std::make_unique(ayarlarDurum_); sketcherPanel_ = std::make_unique(); sketchAraclariPanel_ = std::make_unique(); } // ---- FikoBelgePenceresi::kur() sonu --------------------------------------- // ---- FikoBelgePenceresi::kareCiz() — eski kareGovdesi lambda gövdesi (BİREBİR) --- // return false = kare atlandı (swapchain hazır değil) → senaryo/tail atlanır. bool FikoBelgePenceresi::kareCiz() { g_sonAdim = "kareCiz: UI+girdi"; // faz izleyici (çökme handler cokme.log'a yazar) FIKO_ADIM("kareCiz TAMAMI"); // ── Merkezi süreç yöneticisi (IPC) — ana pencere main-thread pompası (tek/proc) ── if (anaMi_ && ort_.belgePtr) { Cekirdek& cek = ort_.belgePtr->cekirdek; // Heartbeat: durum (meşgul = aktif görev var mı) + belge/uptime/bellek kayda yaz (throttle'lı). u32 calisanGorev = 0; for (const auto& g : cek.gorevler().anlik()) if (g.durum == GorevDurum::Calisiyor) ++calisanGorev; const bool mesgul = calisanGorev > 0; g_surecKopru.durumAyarla(mesgul ? SurecDurum::Mesgul : SurecDurum::Aktif); g_surecKopru.kalpAt(); // Fiko Orchestrator kule: panel açıkken yerel node ilanı + durum türetme // (kapalıyken tek karşılaştırmayla döner — sıcak yola iş eklemez). g_orkestraKule.tik(g_surecKopru, cek.olaylar(), calisanGorev, surecYonPanel_ && surecYonPanel_->acikMi(), GetTickCount64()); // İş Takip servisi (kral/istemci — istakip.* bayrakları kapalıyken sıfır iş). g_istakipServis.tik(cek.olaylar(), GetTickCount64()); // Bu sürecin olaylarını (abone merkez varsa) delta+batch+throttle ile forward et. g_surecKopru.olaylariForwardEt(cek.olaylar()); // Gelen komut/parametre/kapat çerçeveleri (ad→MenuOge→komutCalistir; merkezi gate+audit). for (const IpcKomut& k : g_surecKopru.beklemedekiCerceveler()) { cek.olaylar().yayinla(SurecKomutuAlindi{k.kaynakPid}, k.ad.empty() ? "IPC" : k.ad.c_str()); if (k.tip == SurecMesajTip::Kapat) { // nazik kapanış → WM_CLOSE (kaydet-sor yolu) PostMessageW((HWND)pencere_.dogalTutamac(), WM_CLOSE, 0, 0); continue; } // ── IPC MAKRO KANALI (MCP/otomasyon) ──────────────────────────────── // "makro.lua" özel eylemi: gövdedeki Lua kodunu BU belgede çalıştırır ve // çıktıyı korelasyonlu Onay çerçevesiyle isteyene geri yollar. Yapay zeka // (fiko_mcp) buradan gerçek geometri kurar — menü komutlarıyla sınırlı kalmaz. // LİSANS: kod çalışmadan önce "makro.calistir" hakkı sorulur; hak yoksa // çalıştırılmaz ve gerekçe yanıt olarak döner. Makronun kendi içindeki // fiko.komut.calistir çağrıları ayrıca komutCalistir kapısından geçer. if (k.ad == "makro.lua") { std::string kod; for (const auto& [anahtar, deger] : k.parametreler) if (anahtar == "kod") kod = deger; MenuOge sahteOge; // lisans değerlendirmesi için kimlik taşıyıcı sahteOge.ad = "Makro Çalıştır"; sahteOge.grup = "Makro"; sahteOge.kimlik = "makro.calistir"; // kalıcı lisans kimliği (komutKimligi) const bool izin = g_lisans.izinli(sahteOge); std::string sonuc; bool basarili = false; if (!izin) { sonuc = "Makro çalıştırma hakkı lisansınıza dahil değil (makro.calistir)"; } else if (kod.empty()) { sonuc = "'kod' parametresi boş"; } else { betik::MakroMotor& mk = ort_.belgePtr->makro; mk.ciktiTemizle(); // İSTİSNA GÜVENCESİ: makro içinden gelen C++ istisnası (OCCT, feature // recompute, "resource deadlock") kareyi düşürürse `onayGonder` hiç // koşmaz ve çağıran ZAMAN AŞIMINA düşer — hata görünmez olur. Burada // yakalanır, teşhis adımıyla birlikte yanıta konur (ölçüldü 07-21). try { basarili = mk.satirCalistir(kod); } catch (const std::exception& e) { basarili = false; sonuc = std::string("Makro C++ istisnasi: ") + e.what() + " · son adim: " + (gunluk::g_teshisAdim ? gunluk::g_teshisAdim : "?"); } catch (...) { basarili = false; sonuc = std::string("Makro bilinmeyen istisna · son adim: ") + (gunluk::g_teshisAdim ? gunluk::g_teshisAdim : "?"); } for (const std::string& satir : mk.cikti()) { sonuc += satir; sonuc += '\n'; } if (sonuc.empty()) sonuc = basarili ? "(çıktı yok — makro çalıştı)" : "(hata)"; } if (k.kaynakPid) g_surecKopru.onayGonder(k.kaynakPid, k.korelasyon, basarili, sonuc); continue; } // ── IPC EKRAN GÖRÜNTÜSÜ: 3B viewport'u PNG olarak diske yaz. Yapay zekanın // modellediği şeyi GÖREBİLMESİ için (senaryo modundaki png() ile aynı yol). if (k.ad == "ekran.png") { std::string yol; for (const auto& [anahtar, deger] : k.parametreler) if (anahtar == "yol") yol = deger; bool basarili = false; std::string sonuc; if (yol.empty()) { sonuc = "'yol' parametresi boş"; } else { basarili = vulkan_.viewportPngKaydet(yol.c_str()); sonuc = basarili ? ("PNG yazıldı: " + yol) : ("PNG yazılamadı: " + yol); } if (k.kaynakPid) g_surecKopru.onayGonder(k.kaynakPid, k.korelasyon, basarili, sonuc); continue; } // ── IPC SÜREÇLER-ARASI GÖRÜNÜM SEÇİMİ: başka bir fikocad.exe sürecinden // (Süreç Yöneticisi paneli / MCP) "govde"(+"alt"[+"coklu"]) parametreleriyle // BU belgenin sahnesinde seçim yapar. Yerel viewport tıklamasıyla AYNI yol // (Sahne::sec) — coklu=false önce mevcut seçimi temizler (tekli seçim), // coklu=true toggle/ekler (Sahne::sec semantiği, bkz. sahne.cpp). if (k.ad == "gorunum.sec") { u32 govde = 0, alt = 0; bool coklu = false; for (const auto& [anahtar, deger] : k.parametreler) { if (anahtar == "govde") govde = (u32)std::strtoul(deger.c_str(), nullptr, 10); else if (anahtar == "alt") alt = (u32)std::strtoul(deger.c_str(), nullptr, 10); else if (anahtar == "coklu") coklu = (deger == "1" || deger == "true"); } bool basarili = false; std::string sonuc; if (govde == 0) { sonuc = "'govde' parametresi boş/0"; } else if (!ort_.belgePtr->sahne.bul(govde)) { sonuc = "govde bulunamadı: " + std::to_string(govde); } else { ort_.belgePtr->sahne.sec({govde, alt}, coklu); basarili = true; sonuc = "seçildi: govde=" + std::to_string(govde) + " alt=" + std::to_string(alt); } if (k.kaynakPid) g_surecKopru.onayGonder(k.kaynakPid, k.korelasyon, basarili, sonuc); continue; } // ── IPC "3d Seç" (süreçler-arası) — Kafa penceresi (Ozmo/Diagonal) // butonuyla armlanan süreç TÜM diğer süreçlere bunu yayınlar (bkz. kafa_penceresi.cpp). // Bu süreç kendi ijkUzakKaynakPid'ini set eder; sıradaki 3B tıkta belge_penceresi.cpp // tık zinciri bunu görüp hesapladığı yönü geri gönderir ("ijk.hedef", aşağıda). if (k.ad == "ijk.mod_ac") { if (k.kaynakPid && ort_.bglmPtr) ort_.bglmPtr->ijkUzakKaynakPid = k.kaynakPid; continue; } if (k.ad == "ijk.mod_kapat") { if (ort_.bglmPtr) ort_.bglmPtr->ijkUzakKaynakPid = 0; continue; } // ── IPC "ijk.hedef": başka bir sürecin viewport'unda yakalanan yön BURAYA döner // (bu süreç "ijk.mod_ac" yayınlamıştı). Kafa penceresi bilmediği için bglm'e AYNI // yerel yol gibi yazılır — kafaGuncelle karesinde tüketilir (uygulama_baglam.hpp). if (k.ad == "ijk.hedef") { bool varI = false, varJ = false, varK = false; f64 i = 0, j = 0, kk = 0; for (const auto& [anahtar, deger] : k.parametreler) { if (anahtar == "i") { i = std::strtod(deger.c_str(), nullptr); varI = true; } else if (anahtar == "j") { j = std::strtod(deger.c_str(), nullptr); varJ = true; } else if (anahtar == "k") { kk = std::strtod(deger.c_str(), nullptr); varK = true; } } if (varI && varJ && varK && ort_.bglmPtr) { ort_.bglmPtr->bekleyenIjkHedef = Vec3d{i, j, kk}; ort_.bglmPtr->bilgiVer("Yön diğer süreçten alındı — IJK hedefine yazıldı"); } continue; } for (const MenuOge& o : cek.menuler().hepsi()) if (o.ad == k.ad) { cek.komutCalistir(o, "ipc"); break; } } // Merkeze forward edilmiş olaylar → SurecOlayAlindi re-publish (LisansSistemi/makro tek-merkez) // + dashboard olay akışını besle. for (const ForwardOlay& fo : g_surecKopru.beklemedekiOlaylar()) { cek.olaylar().yayinla(SurecOlayAlindi{fo.kaynakPid, fo.tur, fo.varlik}, fo.ozet); if (surecYonPanel_) surecYonPanel_->olayEkle(fo); } // Orchestrator uzak komutları: kule ROL kapısını (Operatör+) geçenler → merkezi // komut akışı. komutCalistir İKİNCİ kapı (lisans) — uzaktan lisans bypass yok. // ad→MenuOge eşleşmezse sessiz atla (bilinmeyen komut yürütülmez). for (const OrkestraKule::UzakKomut& uk : g_orkestraKule.beklemedekiUzakKomutlar()) { cek.olaylar().yayinla(SurecKomutuAlindi{(u32)uk.nodeId}, uk.ad.c_str()); for (const MenuOge& o : cek.menuler().hepsi()) if (o.ad == uk.ad) { cek.komutCalistir(o, "orkestra"); break; } } // Bu node'a atanmış GÖREVLER (D3): tür=menü komut adı → yürüt → Bitti/Hata geri. // (İlk sürüm senkron komut; ağır işler GorevSistemi worker'ına sonraki dalga.) for (const OrkestraKule::AtanmisTask& at : g_orkestraKule.beklemedekiAtanmisTasklar()) { bool ok = false; for (const MenuOge& o : cek.menuler().hepsi()) if (o.ad == at.tur) { ok = cek.komutCalistir(o, "orkestra-gorev"); break; } g_orkestraKule.taskTamamla(at.taskId, ok); } } const f32 dt = dt_; // döngüden (calis) hesaplanır const f32 zaman = zaman_; const auto simdi = simdi_; // basış-süresi timer'ları için (void)zaman; Pencere& pencere = pencere_; PencereOrtami& ort = ort_; VulkanBaglam& vulkan = vulkan_; // ---- Sınıf-üye altyapı alias'ları (frame gövdesi bare isimlerini korur) ---- BoruHatlari& hatlar = hatlar_; CizimOnbellek& anaCizim = anaCizim_; BelgeYonetici& belgeYon = belgeYon_; girdi::FareJestSistemi& jestSis = jestSis_; girdi::SahneHoverProbu& hoverProbu = hoverProbu_; girdi::BaglamPolitika& baglamPol = baglamPol_; girdi::PolitikaKarar& sonPolitika = sonPolitika_; ImVec2& sonPolitikaKonum = sonPolitikaKonum_; u32& sonPolitikaHedef = sonPolitikaHedef_; u32& sonPolitikaHedefAlt = sonPolitikaHedefAlt_; bool& baglamMenuAcildi = baglamMenuAcildi_; ui::AracCubuguSistemi& g_araclar = g_araclar_; ui::SeritCubugu& g_serit = g_serit_; AyarlarPencereDurum& ayarlarDurum = ayarlarDurum_; std::vector& hizliDunyaNokta = hizliDunyaNokta_; const f32 kareSuresiMs = kareSuresiMs_; const int kare = kare_; // Araç kipi (tek araç ana pencere): ana kabuk — 3D Görünüm, ürün ağacı, şerit, // durum çubuğu, ek belge pencereleri — çizilmez ve ana viewport HİÇ raster // edilmez (boşuna GPU işi yok). Yalnız aracın kendi penceresi görünür. const bool aracKipi = aracSistemi().aracKipiMi(); // ---- Per-window alias bloğu: gövde bare isimleri PARAM ort'un belge/state/bglm/ // imgui'sinden alır (eski kareGovdesi captured alias'larının aynısı). Tek pencerede // BİREBİR; çok-pencerede her pencere kendi verisini kullanır. ---- ImGuiKatman& arayuz = *ort.imguiPtr; Belge& belge = *ort.belgePtr; UygulamaBaglam& bglm = *ort.bglmPtr; // İmleç/koordinat yardımcıları bglm'den (per-window doğru belge'ye bağlı; // yakalanan calistir-yerel'lerini gölgeler). Ana pencerede bglm.X == yerel X. auto& imlecDunya = bglm.imlecDunya; auto& imlecYuzey = bglm.imlecYuzey; auto& imlecMerkez = bglm.imlecMerkez; auto& imlecVurus = bglm.imlecVurus; auto& uvDunya = bglm.uvDunya; auto& ekranUV = bglm.ekranUV; auto& sketchEnYakin = bglm.sketchEnYakin; auto& sketchAracTikSayi = bglm.sketchAracTikSayi; auto& sketchAracTamamla = bglm.sketchAracTamamla; auto& sketchNesne = bglm.sketchNesne; auto& sketchDuzenMi = bglm.sketchDuzenMi; auto& sketchSecimYay = bglm.sketchSecimYay; auto& sketchDuzenIlerlet = bglm.sketchDuzenIlerlet; // düzenleme fazı ilerlet (Enter/Devam) SecimGpu& secim = belge.secim; Cekirdek& cekirdek = belge.cekirdek; Sahne& sahne = belge.sahne; SecimSistemi& secimSis = belge.secimSis; OlcumSistemi& olcum = belge.olcum; AlanSecim& alan = belge.alan; betik::MakroMotor& makro = belge.makro; eklenti::EklentiYonetici& eklentiler = belge.eklentiler; cam::CamSistemi& camSis = belge.camSis; parca::PartDesignSistemi& partD = belge.partD; gsd::GsdSistemi& gsdSis = belge.gsdSis; analiz::AnalizSistemi& analizSis = belge.analizSis; DokumSistemi& dokumSis = belge.dokumSis; SahneRefSecim& refSecim = belge.refSecim; OrbitKamera& kamera = belge.kamera; FareDurum& fare = ort.fare; HizliUIDurum& hizliDurum = ort.hizliDurum; bool& hizli3dAcik = hizliDurum.hizli3dAcik; u32& hizli3dKimlik = hizliDurum.hizli3dKimlik; int& hizliKutukMod = hizliDurum.hizliKutukMod; bool& hizliKutukSahne = hizliDurum.hizliKutukSahne; f32& hizliKutukSaydam = hizliDurum.hizliKutukSaydam; int& hizliYonSecim = hizliDurum.hizliYonSecim; bool& pickFiltreYedekVar = ort.pickFiltreYedekVar; bool& pickYedYuz = ort.pickYedYuz; bool& pickYedKenar = ort.pickYedKenar; bool& pickYedNokta = ort.pickYedNokta; bool& pickYedGovde = ort.pickYedGovde; Vec3f& hizliYon1 = hizliDurum.hizliYon1; Vec3f& hizliYon2 = hizliDurum.hizliYon2; bool& hizliYon1Var = hizliDurum.hizliYon1Var; bool& hizliYon2Var = hizliDurum.hizliYon2Var; Vec3f (&hizliKose)[8] = hizliDurum.hizliKose; bool& hizliKutuVar = hizliDurum.hizliKutuVar; Vec3f& hizliBoyut = hizliDurum.hizliBoyut; PusulaDurum& pusula = ort.pusula; KesitGizmoDurum& kesitGizmo = ort.kesitGizmo; SecimYigini& secimYigini = ort.secimYigini; bool& hizliGorunumAcik = ort.hizliGorunumAcik; IsikDurum& isikDurum = ort.isikDurum; bool& panelIsik = ort.panelIsik; bool& isikUstte = ort.isikUstte; bool& panelAgac = ort.panelAgac; bool& panelOzellik = ort.panelOzellik; bool& panelOlay = ort.panelOlay; bool& agacIcinde = ort.agacIcinde; bool& agacOdakta = ort.agacOdakta; f32& agacSahneOlcek = ort.agacSahneOlcek; f32& agacPanDelta = ort.agacPanDelta; bool& agacSigdir = ort.agacSigdir; bool& ozellikAcikOnceki = ort.ozellikAcikOnceki; bool& agacKaydirGerek = ort.agacKaydirGerek; bool& agacUstte = ort.agacUstte; bool& ozellikUstte = ort.ozellikUstte; bool& olayUstte = ort.olayUstte; bool& islerUstte = ort.islerUstte; bool& olcumUstte = ort.olcumUstte; bool& camUstte = ort.camUstte; bool& panelIsler = ort.panelIsler; bool& panelMakro = ort.panelMakro; bool& panelEklenti = ort.panelEklenti; SketchUIDurum& sketchDurum = ort.sketchDurum; PaftaUIDurum& paftaDurum = ort.paftaDurum; Varlik& aktifPafta = paftaDurum.aktifPafta; // Teknik resim modunda 3B sahne DONDURULUR: fare jestleri (tık/sürükle/tekerlek/ // çift-tık) yalnız sayfaya gider, arkadaki sahneye SIZMAZ (kamera oynamaz, seçim // değişmez, pick/hover koşmaz → bedava perf). Sketch'ten farkı: sahne hiç çizilmez // sayılmaz, yalnız girdi kapatılır (kamera geri dönüşü çıkışta aynen yapılır). const bool paftaModda = paftaDurum.modda(); Varlik& aktifSketch = sketchDurum.aktifSketch; bool& panelSketch = sketchDurum.panelSketch; std::string& sketchArac = sketchDurum.sketchArac; std::vector>& sketchNoktalar = sketchDurum.sketchNoktalar; std::vector& sketchSecili = sketchDurum.sketchSecili; std::vector& sketchSeciliKisit = sketchDurum.sketchSeciliKisit; eskiz::SnapSonuc& sonSnap = sketchDurum.sonSnap; bool& sketchYapiMod = sketchDurum.sketchYapiMod; bool& sketchOtoGeoKisit = sketchDurum.sketchOtoGeoKisit; int& sketchPolyKenar = sketchDurum.sketchPolyKenar; int& sketchDuzenFaz = sketchDurum.sketchDuzenFaz; std::array& sketchDuzenRef = sketchDurum.sketchDuzenRef; char (&sketchDuzGiris)[4][32] = sketchDurum.sketchDuzGiris; bool& pah3DAcik = ort.pah3DAcik; bool& pah3DYuvarlat = ort.pah3DYuvarlat; char (&pah3DGiris)[32] = ort.pah3DGiris; size_t& oncekiIsSayi = ort.oncekiIsSayi; u32& olcumSurukle = ort.olcumSurukle; int& olcumSurukleMod = ort.olcumSurukleMod; DugumPusula& ozellikEski = ort.ozellikEski; std::string& durumMesaj = ort.durumMesaj; f32& durumMesajBitis = ort.durumMesajBitis; f32& simdikiZaman = ort.simdikiZaman; f32& sonEnBoy = ort.sonEnBoy; f32& sonG = ort.sonG; f32& sonY_ = ort.sonY_; uint64_t& vpDoku = ort.vpDoku; VkImageView& vpKayitliGorunum = ort.vpKayitliGorunum; u32& istenenVpG = ort.istenenVpG; u32& istenenVpY = ort.istenenVpY; f32& vpRectMinX = ort.vpRectMinX; f32& vpRectMinY = ort.vpRectMinY; bool& vpHover = ort.vpHover; bool& dockKuruldu = ort.dockKuruldu; bool& hoverBilgiAcik = ort.hoverBilgiAcik; auto& bilgiVer = bglm.bilgiVer; auto& tumunuSigdir = bglm.tumunuSigdir; auto& secimiSigdir = bglm.secimiSigdir; auto& seciliSil = bglm.seciliSil; auto& gorunurDegistir = bglm.gorunurDegistir; auto& boolIslem = bglm.boolIslem; // ---- Çok-pencere: BU pencerenin ImGui context'ini aktif yap + render hedefini // kendi yüzeyi/viewport'una yönlendir. Ana pencere → varsayılan (ana yüzey); // ek pencere → kendi PencereYuzeyi + ViewportHedef (aktifYuzeyAyarla). ---- arayuz.ctxAktif(); if (anaMi_) vulkan.aktifSifirla(); else vulkan.aktifYuzeyAyarla(yuzeySahip_.get(), vpSahip_.get()); kamera.guncelle(dt); ort.metrikToplayici.guncelle(dt, kareSuresiMs); // Performans panosu (her kare, ~2Hz ağır örnek) // Tema değişimini izle (galeri/Lua hangi yoldan değiştirirse değiştirsin) // → kalıcı ayara yaz (ptr karşılaştırma: değişmedikçe bedava). { static const char* sonTemaAd = ui::tema().ad; if (ui::tema().ad != sonTemaAd) { sonTemaAd = ui::tema().ad; ayarYaz("tema", sonTemaAd); } } // Ölçü çizgisi varsayılan yönü ekrana baksın diye kamera bazisini ölçüme ver. olcum.kameraSag = kamera.yonelim().dondur({1, 0, 0}); olcum.kameraYukari = kamera.yonelim().dondur({0, 1, 0}); sahne.bekleyenleriYenidenInsa(); // ozellik degisimiyle isaretlenen dugumler (kok/reaktif) belge.partD.asyncBekleyenUygula(); // DM async apply: biten OCCT işleri sonuçlarını indirir { // Perf-kritik ayarların ETKİN değerleri (bir kez): "neden yavaş" teşhisi log'dan yapılır. static bool ayarLoglandi = false; if (!ayarLoglandi) { ayarLoglandi = true; gunluk::bilgi("[ayar] kaba_async={} sapma_kuantala={} async_apply={} usd_esik={} " "fillet_deneme={}ms", kabaMeshArkaplan(), geometri::sapmaKuantala(), dogrudanAyar().asyncApply, geometri::usdYuzEsigi(), geometri::filletDenemeMs()); } } // Tamamlanan arka plan islerinin finalize'i (GPU yukleme/sahne) burada, // kare kaydi baslamadan once main-thread'de kosar; GorevBasladi/Bitti yayinlar. cekirdek.gorevler().pompala(cekirdek.olaylar()); // Makro per-frame callback'leri (fiko.guncelle) + eklenti kareGuncelle. makro.kareGuncelle(dt); eklentiler.kareGuncelle(dt); if (anaMi_) otoKayit().kare(dt); // sıcak yol: toplama + eşik (kirli değilse hiç) // Parametrik/link zinciri: kirli nesneleri topolojik sırada yeniden hesapla. if (cekirdek.recompute().kirliVar()) cekirdek.recompute().hesapla(cekirdek, cekirdek.olaylar()); // ---- Hızlı 3D kütük kutusu: kare başına BİR kez (sahne overlay + panel aynı veri) ---- // İmza-önbellek: girdiler (gövde/mod/yön/matris/mesh) değişmedikçe O(n) PCA/ // dönüşüm YENİDEN KOŞMAZ (büyük mesh'te panel açıkken kare bütçesi korunur). hizliKutuVar = false; if (hizli3dAcik || hizliKutukSahne) { // Kütük/ağırlık yalnız mesh'li gövde için anlamlı; eksen vb. tip-özel widget'lar // (indeksSayi==0) bu bloğa girmez (dejenere sınır → NaN koruması). if (Dugum* hd = sahne.bul(hizli3dKimlik); hd && hd->indeksSayi > 0) { const Mat4f M = hd->matris(); static u64 hizliImza = 0; static bool hizliSonVar = false; u64 im = 1469598103934665603ull; const auto kat = [&](u64 v) { im = (im ^ v) * 1099511628211ull; }; kat(hizli3dKimlik); kat((u64)hizliKutukMod); kat((u64)hizliYon1Var * 2 + (u64)hizliYon2Var); const auto katF = [&](f32 f) { u32 b; std::memcpy(&b, &f, 4); kat(b); }; katF(hizliYon1.x); katF(hizliYon1.y); katF(hizliYon1.z); katF(hizliYon2.x); katF(hizliYon2.y); katF(hizliYon2.z); for (int i = 0; i < 16; ++i) katF((&M.e[0][0])[i]); kat((u64)hd->cpuKonum.size()); kat((u64)reinterpret_cast(hd->cpuKonum.data())); katF(hd->yerelSinir.asgari.x); katF(hd->yerelSinir.azami.x); katF(hd->yerelSinir.asgari.y); katF(hd->yerelSinir.azami.y); katF(hd->yerelSinir.asgari.z); katF(hd->yerelSinir.azami.z); if (im == hizliImza) { hizliKutuVar = hizliSonVar; // önbellek geçerli (hizliKose/hizliBoyut korunur) } else { hizliImza = im; if (hizliKutukMod == 2 && hizliYon1Var) { hizliDunyaNokta.clear(); hizliDunyaNokta.reserve(hd->cpuKonum.size()); for (const Vec3f& p : hd->cpuKonum) { const Vec4f w = M * Vec4f{p.x, p.y, p.z, 1.0f}; hizliDunyaNokta.push_back({w.x, w.y, w.z}); } const KutukKutu k = kutukKutuYon(hizliDunyaNokta, hizliYon1, hizliYon2Var, hizliYon2); if (k.gecerli) { for (int i = 0; i < 8; ++i) hizliKose[i] = k.kose[i]; hizliKutuVar = true; } } else { const KutukKutu k = hizliKutukMod == 1 ? kutukKutuObb(hd->cpuKonum) : kutukKutuAabb(hd->yerelSinir); if (k.gecerli) { for (int i = 0; i < 8; ++i) { const Vec4f w = M * Vec4f{k.kose[i].x, k.kose[i].y, k.kose[i].z, 1.0f}; hizliKose[i] = {w.x, w.y, w.z}; } hizliKutuVar = true; } } if (hizliKutuVar) hizliBoyut = kutukBoyutKoseler(hizliKose); hizliSonVar = hizliKutuVar; } // imza-önbellek } } // Viewport hedefini 3D pencerenin son isteğine göre ayarla (1 kare gecikme). // Sürüklerken (WM_ENTERSIZEMOVE) ATLA: her ara boyutta offscreen viewport+secim'i // yeniden yaratmak kare başına 2 ek vkDeviceWaitIdle demek → sürüklerken takılma/ // donma. ImGui mevcut dokuyu yeni alana esnetir (hafif yumuşak); sürükleme bitince // (WM_EXITSIZEMOVE) tek seferde gerçek çözünürlüğe gelir. Swapchain zaten present // için her boyutta güncellenir (o zorunlu); ertelenen yalnız offscreen hedef. const bool boyutlaniyor = pencere_.boyutlaniyorMu(); // Araç kipi: pencere ÖLÇÜSÜ VE KONUMU kullanıcı ayarıdır — taşıyıp boyutlandırınca // araç başına kalıcı ayara yazılır, bir dahaki açılış aynı yer/ölçüde gelir. // Kamera duruşu kalıbının aynısı: sıcak yolda IO yok, yalnız geometri DEĞİŞİP // 1 sn sabit kalınca TEK yazma. Ekran dışı konum aracPencereGeoYaz'da elenir. // static: araç kipi tanımı gereği tek pencerelidir (çok-pencere yolu buraya girmez). if (aracKipi && !boyutlaniyor) { static i32 sonX = 0, sonY0 = 0; static u32 sonG = 0, sonY = 0; static f32 yazBekle = -1.0f; // <0 = yazıldı/beklemede değil const u32 g = pencere_.genislik(), y = pencere_.yukseklik(); RECT wr{}; GetWindowRect((HWND)pencere_.dogalTutamac(), &wr); if (g >= 120 && y >= 120) { if (g != sonG || y != sonY || wr.left != sonX || wr.top != sonY0) { sonG = g; sonY = y; sonX = wr.left; sonY0 = wr.top; yazBekle = 0.0f; } else if (yazBekle >= 0.0f) { yazBekle += dt; if (yazBekle > 1.0f) { yazBekle = -1.0f; aracPencereGenislikAyarla((int)g); // genel varsayılan da tazelensin aracPencereYukseklikAyarla((int)y); aracPencereGeoYaz(aracSistemi().aktifKimlik(), sonX, sonY0, (int)g, (int)y); } } } } // Araç kipinde ana viewport hiç gösterilmez → en küçük hedefte tut (VRAM + pick // tamponu boşuna büyümesin). Hedef yine geçerli kalır: belgeCiz atlansa da // kaynaklar kurulu olsun (aracı kapatıp tam CAD'e dönme yolu bozulmasın). if (aracKipi) { istenenVpG = 16; istenenVpY = 16; } if (!boyutlaniyor) { vulkan.viewportBoyut(istenenVpG, istenenVpY); secim.boyutAyarla(vulkan.viewportBoyutu().width, vulkan.viewportBoyutu().height); } if (!aracKipi && vpKayitliGorunum != vulkan.viewportRenkGorunum()) { if (vpDoku) arayuz.dokuKaldir(vpDoku); vpDoku = arayuz.dokuEkle(vulkan.viewportOrnekleyici(), vulkan.viewportRenkGorunum()); vpKayitliGorunum = vulkan.viewportRenkGorunum(); } // Ek belgeler: hedef boyut + kamera yumuşatma + belge-scope kare bakımı + doku belgeHedefleriniGuncelle(belgeYon, arayuz, vulkan, dt, boyutlaniyor); // Et kalınlığı per-fragment ışın alanı (set=1 BVH/SSBO) — KOMUT TAMPONU DIŞINDA // (descriptor yazımı + olası device-idle kayıt ortasında yapılamaz). kalinlikIsinAlaniGuncelle(sahne, analizSis, hatlar_); // Malzeme GPU tabloları (set=1) — AYNI GEREKÇE ile burada: tampon büyütmede // device-idle var; komut kaydı ortasında çağrılırsa fence zaman aşımı → AYGIT // KAYBI (07-20'de bire bir yaşandı, açılışta kapanıyordu). if (ort_.belgePtr) hatlar_.malzemeGpu.tazele(ort_.belgePtr->malzemeKut, ort_.belgePtr->malzemeAta, sahne); kafaGuncelle(arayuz, vulkan, dt, shaderDizin_, bglm); // Diagonal + Ozmo kafa mini-viewport #ifdef FIKO_CEF yardimCefPompa(); // CEF mesaj işi (OnPaint/girdi ANA thread'de → doku doğrudan yüklenir) #endif arayuz.kareBasla(); uiStiliUygula(); // global UI ince ayarları (yazı boyutu/boşluk/köşe/tema) → ImGui stili ImGuiIO& io = ImGui::GetIO(); const f32 ekranG = io.DisplaySize.x, ekranY = io.DisplaySize.y; const SecimKip kip = sahne.kip(); (void)kip; // seçim kipi göstergesi kaldırıldı; kip başka dallarda kullanılabilir // ---- Komut paleti: Ctrl+Space toggle VEYA boş-yere-sağ-tık (açılışta registry'den beslenir) ---- if ((io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_Space)) || ort.bosSagTikArama) { const bool sagTik = ort.bosSagTikArama; ort.bosSagTikArama = false; if (ort.komutPalet.acikMi() && !sagTik) { ort.komutPalet.kapat(); } else if (!ort.komutPalet.acikMi()) { // Universal palet: komut + sahne nesnesi tek fuzzy aramada (kaynaklar canlı). ui::universalKaynaklariKur(ort.universalArama, ort.paletKayit, cekirdek, sahne, [&bglm](u32) { bglm.secimiSigdir(); }, g_sonDosyalar, [&bglm](const std::string& yol) { fiko::yolAc(bglm, yol); }); ort.komutPalet.baglaUniversal(&ort.universalArama); ort.komutPalet.ac(); } } // ---- Seçim kipi kısayolları (Plasticity: 1=Nokta 2=Kenar 3=Yüz 4=Gövde) ---- // Yalnız metin girişi/palet kapalı + modifier yokken → başka kısayollarla çakışmaz. if (!io.WantTextInput && !ort.komutPalet.acikMi() && !io.KeyCtrl && !io.KeyAlt && !io.KeyShift) { if (ImGui::IsKeyPressed(ImGuiKey_1, false)) sahne.kipAyarla(SecimKip::Nokta); else if (ImGui::IsKeyPressed(ImGuiKey_2, false)) sahne.kipAyarla(SecimKip::Kenar); else if (ImGui::IsKeyPressed(ImGuiKey_3, false)) sahne.kipAyarla(SecimKip::Yuz); else if (ImGui::IsKeyPressed(ImGuiKey_4, false)) sahne.kipAyarla(SecimKip::Govde); } // ---- Isolation (Alt+H): seçili gövde hariç gizle / tekrar bas → geri al (Plasticity) ---- if (io.KeyAlt && !io.KeyCtrl && !io.WantTextInput && ImGui::IsKeyPressed(ImGuiKey_H, false)) { if (ort.izoleAktif) { // geri al: yalıtım öncesi görünürlüğü yükle for (const auto& [k, g] : ort.izoleYedek) gorunurDegistir(k, g); ort.izoleYedek.clear(); ort.izoleAktif = false; } else { // yalıt: seçili gövde(ler) hariç hepsini gizle, önceki durumu sakla ort.izoleYedek.clear(); bool secimVar = false; for (Dugum& d : sahne.dugumler()) if (sahne.govdeSeciliMi(d.kimlik)) { secimVar = true; break; } if (secimVar) { for (Dugum& d : sahne.dugumler()) { ort.izoleYedek.push_back({d.kimlik, d.gorunur}); if (!sahne.govdeSeciliMi(d.kimlik) && d.gorunur) gorunurDegistir(d.kimlik, false); } ort.izoleAktif = true; } } } // (Seçim kipi göstergesi kaldırıldı — sağ-alt "◈ Gövde [4]" pill; kullanıcı istemedi.) // ---- Dockspace ana çerçeve (CATIA/Unreal editör düzeni) ---- // Araç kipinde HİÇ çizilmez: tüm ekranı kaplayan gri host penceresi, aracın cam // zeminli penceresinin ÜSTÜNDE kalıp (NoBringToFrontOnFocus) onu soluklaştırıyordu. const ImGuiViewport* anaVp = ImGui::GetMainViewport(); if (!aracKipi) { ImGui::SetNextWindowPos(anaVp->WorkPos); ImGui::SetNextWindowSize(anaVp->WorkSize); ImGui::SetNextWindowViewport(anaVp->ID); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); ImGui::Begin("##dockhost", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus); ImGui::PopStyleVar(3); const ImGuiID dockKimlik = ImGui::GetID("FikoDock"); ImGui::DockSpace(dockKimlik, ImVec2(0, 0), ImGuiDockNodeFlags_PassthruCentralNode); // İlk çalıştırmada varsayılan yerleşim if (!dockKuruldu) { dockKuruldu = true; ImGui::DockBuilderRemoveNode(dockKimlik); ImGui::DockBuilderAddNode(dockKimlik, ImGuiDockNodeFlags_DockSpace); ImGui::DockBuilderSetNodeSize(dockKimlik, anaVp->WorkSize); ImGuiID merkez = dockKimlik; const ImGuiID sol = ImGui::DockBuilderSplitNode(merkez, ImGuiDir_Left, 0.19f, nullptr, &merkez); const ImGuiID alt = ImGui::DockBuilderSplitNode(merkez, ImGuiDir_Down, 0.24f, nullptr, &merkez); ImGui::DockBuilderDockWindow("Ürün Ağacı", sol); ImGui::DockBuilderDockWindow("Olay Denetçi", alt); ImGui::DockBuilderDockWindow("3D Görünüm", merkez); // 3D görünüm merkez düğümü sabit: tab çubuğu gizli + undock/split engelli. if (ImGuiDockNode* merkezNode = ImGui::DockBuilderGetNode(merkez)) merkezNode->LocalFlags |= ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_NoUndocking | ImGuiDockNodeFlags_NoDockingSplit; ImGui::DockBuilderFinish(dockKimlik); } // ---- Üst menü çubuğu KALDIRILDI — tüm işlemler Ctrl+Space Kontrol Merkezi'nde // (paneller, seçim filtreleri, trap, workbench, compass, komut araması). ---- ImGui::End(); // dockhost altYatayAracCubugu(anaVp, bglm); // toolbar: dockhost DIŞINDA — arka plan + border görünür } // if (!aracKipi) — dockspace ana çerçeve // ---- 3D Görünüm penceresi (MDI child, offscreen doku) ---- ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); // NoBringToFrontOnFocus: 3D görünüme tıklamak onu yüzen panellerin üstüne çıkarmaz // → ayrık paneller (Ürün Ağacı vb.) varsayılan olarak hep önde kalır. // NoMove: 3D görünüm sürüklenip taşınamaz / ayrı OS penceresine çıkarılamaz (sabit merkez). // Araç kipi: Begin HİÇ çağrılmaz → pencere de içindeki ürün ağacı overlay'i de // yok. Kısa devre kasıtlı; aşağıdaki End() aynı koşulla atlanır (ImGui dengesi). if (!aracKipi && ImGui::Begin("3D Görünüm", nullptr, ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoMove)) { // Dock düğümünü her kare kilitle (ini geri-yüklemesinden bağımsız): tab çubuğu // gizli + tab'dan tutup ayrı pencere/OS-viewport'a sürükleme + node bölme engelli. if (ImGuiDockNode* mNode = ImGui::GetWindowDockNode()) mNode->LocalFlags |= ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_NoUndocking | ImGuiDockNodeFlags_NoDockingSplit; vpHover = ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows); const ImVec2 avail = ImGui::GetContentRegionAvail(); istenenVpG = (u32)std::max(avail.x, 16.0f); istenenVpY = (u32)std::max(avail.y, 16.0f); const ImVec2 imlecKonum = ImGui::GetCursorScreenPos(); vpRectMinX = imlecKonum.x; vpRectMinY = imlecKonum.y; // Arka plan (bulut+logo) sahne dokusundan ÖNCE, aynı drawlist'e → sahne // saydam-temiz viewport ile bunun önüne kompoze olur (derinlik korunur). arkaPlanCiz(ImGui::GetWindowDrawList(), imlecKonum, avail); // ---- İzleme Grid'i (XY düzlemi referans çizgileri) ---- // Grid modelin önünde çıkmasın diye viewport'dan ÖNCE çizelim { ImDrawList* dl = ImGui::GetWindowDrawList(); const float alfa = 40.0f / 255.0f; // yarı saydam (orijinal gibi) ui::webSkinGridCiz(dl, {vpRectMinX, vpRectMinY}, {vpRectMinX + avail.x, vpRectMinY + avail.y}, alfa); } if (vpDoku) ImGui::Image((ImTextureID)vpDoku, avail); // ---- CATIA spec tree: ürün ağacı 3B görünümün İÇİNDE (sol overlay) ---- // Ayrık pencere yerine viewport'a gömülü yarı-saydam panel → z-order // sorunu yapısal olarak yok (aynı pencerenin child'ı, Image'ın hep üstünde). if (panelAgac && agacIcinde) { const float agacGen = 330.0f; const float agacYuk = std::max(160.0f, avail.y - 24.0f); const ImVec2 agacMin{vpRectMinX + 10.0f, vpRectMinY + 12.0f}; const ImVec2 agacMax{agacMin.x + agacGen, agacMin.y + agacYuk}; const bool fareAgacUstunde = ImGui::IsMouseHoveringRect(agacMin, agacMax); // ---- Sol aktivasyon çizgisi (CATIA spec-tree sol dal hattı): TIK → ağaç // odağı/zoom. Öğelerin solundaki dikey hat; satır tıkları seçim için serbest // kalır (artık "her yere tık → odak" YOK). Overlay'in hemen solunda ince şerit. ---- const float seritSagX = agacMin.x - 2.0f; const float seritSolX = seritSagX - 8.0f; ImGui::SetCursorScreenPos({seritSolX, agacMin.y}); ImGui::InvisibleButton("##agacOdakSerit", {8.0f, agacYuk}); const bool seritHover = ImGui::IsItemHovered(); if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) agacOdakta = !agacOdakta; { ImDrawList* odl = ImGui::GetWindowDrawList(); const float lx = (seritSolX + seritSagX) * 0.5f; const ImU32 lc = agacOdakta ? IM_COL32(70, 210, 235, 255) : seritHover ? IM_COL32(150, 200, 225, 220) : IM_COL32(110, 140, 165, 130); odl->AddLine({lx, agacMin.y + 4.0f}, {lx, agacMax.y - 4.0f}, lc, agacOdakta ? 3.0f : (seritHover ? 2.0f : 1.5f)); if (agacOdakta || seritHover) odl->AddCircleFilled({lx, agacMin.y + 10.0f}, 3.0f, lc); } // ---- Odak geçişi (CATIA P2): Shift+F3 toggle · sol çizgi tık (yukarıda) · // ESC → çık. Boş alana tık odaktan ÇIKARMAZ (o tık sahneye gider — orbit). ---- if (ImGui::IsKeyPressed(ImGuiKey_F3, false) && io.KeyShift) agacOdakta = !agacOdakta; if (agacOdakta && ImGui::IsKeyPressed(ImGuiKey_Escape)) agacOdakta = false; // Teknik resim modunda Esc: önce ölçü yerleştirmeyi iptal eder, o da yoksa // 3B'ye döner (ağaç odağı Esc'i tükettiyse bu kare değil). else if (aktifPafta != VARLIK_YOK && ImGui::IsKeyPressed(ImGuiKey_Escape) && !ImGui::IsAnyItemActive()) { if (paftaDurum.olcuFaz != 0) paftaDurum.olcuFaz = 0; else if (paftaDurum.kesitFaz != 0) paftaDurum.kesitFaz = 0; else if (paftaDurum.detayFaz != 0) paftaDurum.detayFaz = 0; else bglm.paftaCik(); } ImGui::SetCursorScreenPos(agacMin); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(6, 6)); // Arka plan DAİMA tam saydam (koyu zemin YOK, border YOK) → ağaç 3B üstünde // yüzer; boş/arka plan kısmında hiçbir görsel yok. Odak göstergesi yalnız sol // aktivasyon çizgisi (cyan). Böylece boş kısım "cam gibi" — arkadaki sahne görünür. ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4{0, 0, 0, 0}); ImGui::SetNextWindowBgAlpha(0.0f); ImGui::BeginChild("##agacOverlay", {agacGen, agacYuk}, false, ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoBackground); const bool agacSatirUstunde = urunAgaciIcerikCiz(bglm); ImGui::EndChild(); ImGui::PopStyleColor(1); ImGui::PopStyleVar(1); // ---- Zoom + Pan: YALNIZ gerçek satır/çizgi üstünde (boş alanda değil) → // ağacın boş kısmında wheel/orbit ARKADAKI SAHNEYE gider (eski davranış). ---- const bool icerikUstunde = agacSatirUstunde || seritHover; if (agacOdakta && icerikUstunde && io.MouseWheel != 0.0f) agacSahneOlcek = std::clamp(agacSahneOlcek + io.MouseWheel * 0.1f, 0.6f, 2.5f); if (agacOdakta && icerikUstunde && ImGui::IsMouseDragging(ImGuiMouseButton_Middle, 1.0f)) agacPanDelta = -io.MouseDelta.y; // içeriği kavrayıp sürükle metaforu // Odak dışıyken de Ctrl+wheel ağaç yazı ölçeği (CATIA §6.5.4) — yalnız içerik üstünde. if (!agacOdakta && agacSatirUstunde && io.KeyCtrl && io.MouseWheel != 0.0f) agacSahneOlcek = std::clamp(agacSahneOlcek + io.MouseWheel * 0.1f, 0.6f, 2.5f); // Model kilidi: kamera/pick YALNIZ gerçek bir ağaç SATIRI VEYA sol çizgi // üstünde bastırılır. Boş/arka plan kısmında fare 3B sahneye geçer → oradan // orbit/pan/zoom (kullanıcı isteği: boş kısımdan sahneyi döndürebilsin). if (agacSatirUstunde || seritHover) vpHover = false; } // ---- CATIA navigasyon ghost'u (döndür=küre / pan=ok / zoom=çift-ok) ---- // Compass'ın ALTINDA çizilir. Kip canlı fare tuşlarından (btn 6→döndür, // 3→zoom, 4→pan — kipEsle ile aynı). Ağaç odaktayken bastırılır. { const int gkip = fare.kip; // merkezi durum makinesinin çözdüğü kip (bir önceki kare) // Tekerlek zoom flaşı YALNIZ 3B görünüm hover'lıyken: başka bir ImGui paneli // (Yardım/WTEC/Ayarlar) üstünde scroll ederken zoom ghost'u belirmesin — kamera // zaten zoom yapmıyor (gerçek zoom `vpHover && !pusulaUstunde` kapısında). const f32 gTeker = (agacOdakta || !vpHover || paftaModda) ? 0.0f : io.MouseWheel; navGhostCizVeIsle(navGhostGlobal(), kamera, ImGui::GetWindowDrawList(), gkip, gkip >= 0 && !agacOdakta, io.DeltaTime, vpRectMinX, vpRectMinY, avail.x, avail.y, sonEnBoy, gTeker); } // ---- CATIA 3B Compass (navigasyon + manipülatör) ---- bool pusulaUstunde = false; // Birleşik gizmo bir gövdeyi yönetiyorsa (govdeMod) compass obje-taşımayı ona bırakır. const bool gizmoGovdeAktif = ort.yuzTasi.aktif && ort.yuzTasi.govdeMod && dogrudanAyar().gizmoCompassOncelik; pusulaCizVeIsle(pusula, sahne, kamera, ImGui::GetWindowDrawList(), io, vpHover, vpRectMinX, vpRectMinY, avail.x, avail.y, sonEnBoy, pusulaUstunde, gizmoGovdeAktif); // ---- ViewCube (sağ üst, compass altı): yüze tıkla → görünüşe snap ---- if (vizyonKupuAktif()) { bool kupUstunde = false; vizyonKupuCizVeIsle(kamera, ImGui::GetWindowDrawList(), io, vpHover, vpRectMinX, vpRectMinY, avail.x, avail.y, kupUstunde); pusulaUstunde = pusulaUstunde || kupUstunde; // kamera/seçim jestlerini bastır } // ---- AKILLI RE-EDIT (merkez tutamaç): fillet/chamfer/it-çek YÜZÜNE tıkla-sürükle → feature // parametresi güncelle. Torus fillet gibi push/pull'ın açılmadığı yüzleri de kapsar. Push/pull // ARTIK bastırılmıyor: it/çek oku da feature yüzünde o feature'ı düzenler (kullanıcı isteği). if (ort.featureReDuzenle.aktif) { bool frUstunde = false; featureReDuzenleCizVeIsle(ort.featureReDuzenle, sahne, belge.partD, kamera, ImGui::GetWindowDrawList(), io, vpHover, vpRectMinX, vpRectMinY, avail.x, avail.y, sonEnBoy, frUstunde); pusulaUstunde = pusulaUstunde || frUstunde; } // ---- Yüz Taşı/Döndür TAM TRANSFORM gizmosu (Move Face): tek düz yüzde 3-eksen translate + // merkez serbest-taşı + 3 döndürme yayı (draft). DM'de OTOMATİK görünür (komut gerekmez). ---- if (ort.yuzTasi.aktif) { bool ytUstunde = false; yuzTasiCizVeIsle(ort.yuzTasi, sahne, cekirdek, belge.partD, belge.mekanizma, kamera, ImGui::GetWindowDrawList(), io, vpHover, vpRectMinX, vpRectMinY, avail.x, avail.y, sonEnBoy, ytUstunde); pusulaUstunde = pusulaUstunde || ytUstunde; // Kenar gizmosu (⟀ eğ + ⇱ uzat) — KG1: görsel+hover; sürükleme KG2'de. bool kgUstunde = false; kenarGizmoCizVeIsle(ort.kenarGizmo, sahne, cekirdek, belge.partD, kamera, ImGui::GetWindowDrawList(), io, vpHover, vpRectMinX, vpRectMinY, avail.x, avail.y, sonEnBoy, kgUstunde); pusulaUstunde = pusulaUstunde || kgUstunde; } // ---- Push/Pull: yuzTasi TEK düz yüzü ele aldığında (govde!=0) bastırılır; silindir/küre/ // çoklu-yüz → push/pull devrede kalır (yuzTasi yalnız tek düz yüz gizmosu). ---- if (ort.pushPull.aktif && !(ort.yuzTasi.aktif && (ort.yuzTasi.govde != 0 || ort.yuzTasi.surukluyor))) { bool ppUstunde = false; pushPullCizVeIsle(ort.pushPull, sahne, cekirdek, belge.partD, kamera, ImGui::GetWindowDrawList(), io, vpHover, vpRectMinX, vpRectMinY, avail.x, avail.y, sonEnBoy, ppUstunde); pusulaUstunde = pusulaUstunde || ppUstunde; // Doğrudan Modelleme workbench'inde Esc mod'u KAPATMAZ (kalıcı doğrudan düzenleme); // diğer bağlamlarda (Part menü toggle) Esc ile çık. if (cekirdek.workbenchler().aktif() != "dogrudan" && ImGui::IsKeyPressed(ImGuiKey_Escape)) { ort.pushPull.aktif = false; ort.pushPull.surukluyor = false; } } // ---- GSD Yüzey Offset gizmosu (Dalga C): tek GSD Yüzey seçiliyken normal boyunca // offset oku. push_pull/yuzTasi sürüklerken bastırılır (çakışma yok — GSD gizmo // yalnız Yüzey DÜĞÜMÜ seçiminde, onlar katı YÜZ seçiminde çalışır). ---- if (!ort.pushPull.surukluyor && !ort.yuzTasi.surukluyor) { bool gzUstunde = false; belge.gsdSis.kaynakGizleAktif = dogrudanAyar().gsdKaynakGizle; // AyarSistemi → motor bayrağı gsdGizmoCizVeIsle(ort.gsdGizmo, sahne, cekirdek, belge.gsdSis, kamera, ImGui::GetWindowDrawList(), io, vpHover, vpRectMinX, vpRectMinY, avail.x, avail.y, sonEnBoy, gzUstunde); pusulaUstunde = pusulaUstunde || gzUstunde; } // ---- Sketch İt/Çek gizmosu (Doğrudan Modelleme): seçili sketch profilini tut-çek ---- if (ort.sketchItCek.aktif) { bool siUstunde = false; sketchItCekCizVeIsle(ort.sketchItCek, sahne, cekirdek, belge.partD, kamera, ImGui::GetWindowDrawList(), io, vpHover, vpRectMinX, vpRectMinY, avail.x, avail.y, sonEnBoy, siUstunde); pusulaUstunde = pusulaUstunde || siUstunde; } // KYA kalıcı kimlik UI'si SAĞ-TIK bağlam menüsüne taşındı (baglam_menu.cpp grafik-özellik // toolbar'ı) — ana sahne kalabalıklaştırılmaz (kullanıcı: sağ-alt overlay yerine sağ-tık). // ---- Makro öneri çipi (viewport üstü): ardışık tekrarı yakala → Lua makro üret ---- // Workbench-bağımsız (tekrar her yerde olabilir). Kabul → makrolar/ klasörüne .lua. { const int mr = makroOneriCipCizVeIsle(ort.makroOneri, ImGui::GetWindowDrawList(), {vpRectMinX, vpRectMinY}, {avail.x, avail.y}); if (mr != 0) pusulaUstunde = true; // çip tık'ı arkadaki seçime gitmesin if (mr == 1) { // Kabul → ÖNCE AD SOR (küçük kutu); yazma/palet kaydı kutunun kapanışında. const auto dizi = ort.makroOneri.kabulEt(); ort.makroAdLua = makroLuaUret(dizi, "Otomatik makro (tekrar yakalandı)"); const int say = (int)makro.makroListele(makroDizin_).size() + 1; std::snprintf(ort.makroAdGiris, sizeof ort.makroAdGiris, "makro_%d", say); ort.makroAdAcik = true; ort.makroAdIlk = true; } if (ort.makroAdAcik) { if (ort.makroAdIlk) ImGui::SetNextWindowPos({vpRectMinX + avail.x * 0.5f - 140, vpRectMinY + 52}); ImGui::SetNextWindowBgAlpha(0.94f); bool acik = true; bool kaydet = false; if (ImGui::Begin("Makro Adı", &acik, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking)) { ImGui::TextDisabled("Kaydedilecek makronun adı:"); if (ort.makroAdIlk) ImGui::SetKeyboardFocusHere(); ImGui::SetNextItemWidth(210); kaydet = ImGui::InputText("##makroAd", ort.makroAdGiris, sizeof ort.makroAdGiris, ImGuiInputTextFlags_EnterReturnsTrue); ImGui::SameLine(); kaydet = ImGui::SmallButton("✔ Kaydet") || kaydet; // Esc/X de KAYDEDER (üretilen makro kaybolmasın — varsayılan ad yeterli). kaydet = kaydet || ImGui::IsKeyPressed(ImGuiKey_Escape) || !acik; } else if (!acik) kaydet = true; ImGui::End(); if (kaydet) { std::string ad = ort.makroAdGiris; for (char& c : ad) // dosya adı güvenliği if (c == '\\' || c == '/' || c == ':' || c == '*' || c == '?' || c == '"' || c == '<' || c == '>' || c == '|') c = '_'; while (!ad.empty() && ad.back() == ' ') ad.pop_back(); if (ad.empty()) ad = "makro"; std::error_code ec; std::filesystem::create_directories(makroDizin_, ec); std::string yol = makroDizin_ + "/" + ad + ".lua"; for (int i = 2; std::filesystem::exists(yol, ec) && i < 100; ++i) // çakışma → _2 yol = makroDizin_ + "/" + ad + "_" + std::to_string(i) + ".lua"; std::ofstream f(yol, std::ios::binary); if (f) { f << ort.makroAdLua; f.close(); makroKomutEkle(cekirdek, makro, yol); // palete ANINDA düşer (arama menüsü) bilgiVer("Makro kaydedildi: " + std::filesystem::path(yol).stem().string() + " — palette \"Makro\""); } else bilgiVer("Makro yazılamadı: " + yol); ort.makroAdAcik = false; ort.makroAdLua.clear(); } ort.makroAdIlk = false; } } // ---- Akıllı seçim öneri çipi (viewport altı): tek yüz/kenar seçince "+6 seç" ---- // DM ruhu: menü açmadan tek dokunuş. Yalnız DM workbench'inde (seçim yayma orada // anlamlı). Çip tıklanırsa nav/seçim jestlerini bastır (arkadaki nesne seçilmesin). if (cekirdek.workbenchler().aktif() == "dogrudan") { if (akilliOneriCipCizVeIsle(ort.akilliOneri, sahne, ImGui::GetWindowDrawList(), {vpRectMinX, vpRectMinY}, {avail.x, avail.y})) { pusulaUstunde = true; // tık çipe gitti → arkadaki nesne seçilmesin bilgiVer(std::to_string(ort.akilliOneri.adet) + " öğe seçime eklendi"); } } // ---- Kenar Fillet/Pah gizmosu (Doğrudan Modelleme): seçili kenarda tıkla-sürükle ---- if (ort.kenarFillet.aktif) { bool kfUstunde = false; kenarFilletCizVeIsle(ort.kenarFillet, sahne, cekirdek, belge.partD, kamera, ImGui::GetWindowDrawList(), io, vpHover, vpRectMinX, vpRectMinY, avail.x, avail.y, sonEnBoy, kfUstunde); pusulaUstunde = pusulaUstunde || kfUstunde; } // ---- Gizmosuz async op ghost'u (Yüz Kaldır): gizmo modundan bağımsız, her kare çiz ---- yuzGhostCiz(ort.yuzGhost, belge.partD, kamera, ImGui::GetWindowDrawList(), vpRectMinX, vpRectMinY, avail.x, avail.y, sonEnBoy); // ---- Kesit düzlemi 3D gizmosu (sürükle = normali boyunca kaydır) ---- // pusulaUstunde OR'lanır → tutamaç üstünde kamera/seçim jestleri bastırılır. kesitGizmoCizVeIsle(analizSis, sahne, kamera, ImGui::GetWindowDrawList(), io, vpHover && !pusulaUstunde, vpRectMinX, vpRectMinY, avail.x, avail.y, sonEnBoy, kesitGizmo, pusulaUstunde, [](u32 k) { return kesitPencereAcikMi(k); }); // Sürükleme bitti → tek DonusumIslem olarak undo geçmişine (ara kareler girmez) if (pusula.surukleTamam) { pusula.surukleTamam = false; if (Dugum* pd = sahne.bul(pusula.surukleGovdeKimlik)) { const Vec3d fk = pd->donusum.konum - pusula.surukleEski.konum; const bool konumDegisti = fk.x * fk.x + fk.y * fk.y + fk.z * fk.z > 1e-14; const bool yonelimDegisti = std::abs(pd->donusum.yonelim.x - pusula.surukleEski.yonelim.x) + std::abs(pd->donusum.yonelim.y - pusula.surukleEski.yonelim.y) + std::abs(pd->donusum.yonelim.z - pusula.surukleEski.yonelim.z) + std::abs(pd->donusum.yonelim.w - pusula.surukleEski.yonelim.w) > 1e-12; if (konumDegisti || yonelimDegisti) cekirdek.uygula(std::make_unique( sahne, pd->kimlik, pusula.surukleEski, pd->donusum)); } } // ---- Ortak seçim-modu HUD (viewport üst-orta) — K9: tek görsel dil ---- // CAM / RefSecim / gelecek seçim modları hepsi buradan çizer: aynı // konum/padding/yuvarlatma; yalnız bağlam vurgusu (kenar rengi) değişir. // sira = üst üste binmeyi önleyen istif indeksi (0=üst, 1=altına...). const auto secimHudCiz = [&](const char* metin, ImU32 kenarRenk, int sira) { if (!metin || !metin[0]) return; ImDrawList* hudCiz = ImGui::GetWindowDrawList(); const ImVec2 ts = ImGui::CalcTextSize(metin); const f32 padX = 12.0f, padY = 6.0f; const f32 cx = vpRectMinX + avail.x * 0.5f; const f32 ustY = vpRectMinY + 14.0f + sira * (ts.y + padY * 2 + 8.0f); const ImVec2 min{cx - ts.x * 0.5f - padX, ustY}; const ImVec2 max{cx + ts.x * 0.5f + padX, min.y + ts.y + padY * 2}; hudCiz->AddRectFilled(min, max, IM_COL32(18, 24, 28, 228), 6.0f); hudCiz->AddRect(min, max, kenarRenk, 6.0f, 0, 1.5f); hudCiz->AddText({min.x + padX, min.y + padY}, IM_COL32(232, 244, 250, 255), metin); }; // Aktif modlar istifle: CAM (cyan) üstte, RefSecim (yeşil) altına. int hudSira = 0; if (camSis.secim().aktif()) secimHudCiz(camSis.secim().hudMetin(), IM_COL32(26, 217, 255, 220), hudSira++); if (analizSis.secimAktif()) secimHudCiz(analizSis.hudMetin(), IM_COL32(120, 200, 120, 230), hudSira++); // RefSecim rozeti: 🎯 + hud + beklenen tür etiketi (mod-unut panzehiri, vurgulu yeşil). if (refSecim.aktif()) { const int bt = refSecim.beklenenTur(); const char* turEt = bt == 2 ? "KENAR" : bt == 1 ? "YÜZ" : bt == 3 ? "NOKTA" : "SERBEST"; char refHud[192]; std::snprintf(refHud, sizeof(refHud), "🎯 %s · [%s]", refSecim.hudMetin(), turEt); secimHudCiz(refHud, IM_COL32(80, 240, 130, 240), hudSira++); if (vpHover) ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); // mod aktif ipucu } // ---- Ölçüm overlay: dünya→ekran projeksiyon + ImGui drawlist ---- // Ayrı GPU pipeline yok: kare başına yalnız N nokta projeksiyonu (önemsiz). // Kalıcı ölçümler + canlı önizleme; etiket sürükleme (ofset) + sağ sürükleme (döndür). { ImDrawList* ciz = ImGui::GetWindowDrawList(); const Mat4f olcVp = kamera.izdusum(sonEnBoy) * kamera.gorunum(); const auto ekrana = [&](const Vec3f& p, ImVec2& s) -> bool { const Vec4f h = olcVp * Vec4f{p.x, p.y, p.z, 1.0f}; if (h.w <= 1e-4f) return false; s.x = vpRectMinX + (h.x / h.w * 0.5f + 0.5f) * avail.x; s.y = vpRectMinY + (h.y / h.w * 0.5f + 0.5f) * avail.y; return true; }; // ---- Selection Stack (CATIA "Other Selection"): liste + 3B daire + canlı önizleme ---- if (secimYigini.acikMi()) { // Tekerlek + ok ile gez — vurguAyarla/peek ile AYNI karede (tutarlı, kaymasız). if (ImGui::GetIO().MouseWheel != 0.0f) secimYigini.gez(ImGui::GetIO().MouseWheel > 0.0f ? -1 : 1); if (ImGui::IsKeyPressed(ImGuiKey_DownArrow)) secimYigini.gez(1); if (ImGui::IsKeyPressed(ImGuiKey_UpArrow)) secimYigini.gez(-1); bool sec = secimYigini.ciz(ciz, ekrana); if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter)) sec = true; if (const SecimAday* aday = secimYigini.aktifAday()) { secimSis.vurguAyarla(aday->govde, aday->alt, aday->tur); // gezilen aday canlı highlight kareIste(2); if (sec) { // tık/Enter → aday gerçek seçime çevrilir secimTiklaKaydet(cekirdek, sahne, secimSis, ImGui::GetIO().KeyCtrl); secimYigini.kapat(); secimSis.hoverBastir(false); } } } // ---- 3B PMI/GD&T overlay (billboard) → pmi_ciz.cpp ---- { PmiCizimBaglam pcb2; pcb2.ciz = ciz; pcb2.io = &io; pcb2.vpHover = vpHover; pcb2.vpRectMinX = vpRectMinX; pcb2.vpRectMinY = vpRectMinY; pcb2.ekrana = ekrana; pcb2.kameraMesafe = kamera.mesafe(); pcb2.kameraGoz = kamera.konum(); pmiOverlayCiz(bglm, ort.pmiDurum, pcb2); } // ---- Sahne ref-eksen + trap overlay -> sahne_overlay.cpp ---- sahneRefTrapOverlayCiz(sahne, kamera, alan, ciz, io, vpRectMinX, vpRectMinY, avail.x, avail.y, sonEnBoy); // ---- CAD Fark sinematik tur + maks-sapma işaretçisi -> fark_tur.cpp ---- farkTurCiz(analizSis, sahne, kamera, (f32)ImGui::GetTime(), ciz, ekrana, vpRectMinX, vpRectMinY, avail.x, avail.y); // ---- Sketch overlay → sketch_ui.cpp (sketchOverlayCiz), davranış birebir ---- // Kare-yerel çizim durumu (ciz/ekrana/io/vpHover/vpRect + sketch helper'ları) // SketchCizimBaglam ile geçilir; blok gövdesi modüle taşındı. { SketchCizimBaglam scb; scb.ciz = ciz; scb.io = &io; scb.vpHover = vpHover; scb.vpRectMinX = vpRectMinX; scb.vpRectMinY = vpRectMinY; scb.vpBoyutX = avail.x; scb.vpBoyutY = avail.y; scb.ekrana = ekrana; scb.uvDunya = uvDunya; scb.ekranUV = ekranUV; scb.sketchEnYakin = sketchEnYakin; sketchOverlayCiz(bglm, scb); } // ---- Teknik resim (pafta) modu: 3B'nin ÜSTÜNE 2B sayfa → pafta_ciz.cpp ---- if (paftaDurum.modda()) { PaftaCizimBaglam pcb; pcb.ciz = ciz; pcb.io = &io; pcb.vpHover = vpHover; pcb.vpRectMinX = vpRectMinX; pcb.vpRectMinY = vpRectMinY; pcb.vpBoyutX = avail.x; pcb.vpBoyutY = avail.y; paftaCizVeIsle(bglm, paftaDurum, pcb); } // Sketch Analizi penceresi → sketch_ui.cpp::sketchAnalizPenceresiCiz (gelişmiş, // tablo+düzeltici aksiyonlu). Eski satır-içi mükerrer pencere kaldırıldı. // ---- Kütük kutusu overlay (sahnede tel-kafes + ayarlı saydam dolgu) ---- // Köşeler kare başında dünya-uzayında hesaplandı (hizliKose). Burada yalnız projeksiyon. if (hizliKutukSahne && hizliKutuVar) { ImVec2 ek[8]; bool gecerli = true; for (int i = 0; i < 8; ++i) if (!ekrana(hizliKose[i], ek[i])) { gecerli = false; break; } if (gecerli) { static const int kenar[12][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 4}, {1, 5}, {2, 6}, {3, 7}}; const ImU32 renk = IM_COL32(64, 240, 224, 235); const int da = (int)(std::clamp(hizliKutukSaydam, 0.0f, 0.6f) * 255.0f); const ImU32 dolgu = IM_COL32(64, 240, 224, da); if (da > 1) { ciz->AddQuadFilled(ek[0], ek[1], ek[2], ek[3], dolgu); ciz->AddQuadFilled(ek[4], ek[5], ek[6], ek[7], dolgu); ciz->AddQuadFilled(ek[0], ek[1], ek[5], ek[4], dolgu); ciz->AddQuadFilled(ek[3], ek[2], ek[6], ek[7], dolgu); } for (auto& e : kenar) ciz->AddLine(ek[e[0]], ek[e[1]], renk, 1.6f); for (int i = 0; i < 8; ++i) ciz->AddCircleFilled(ek[i], 2.6f, IM_COL32(120, 255, 240, 255)); } } // ---- Olcum 3D overlay -> olcum_penceresi.cpp (kareCizden cikarildi) ---- olcumOverlayCiz(olcum, ekrana, imlecVurus, ciz, io, vpHover, vpRectMinX, vpRectMinY, olcumSurukle, olcumSurukleMod, fare.kameraKullandi); } // ---- Kamera + seçim girdisi: CATIA/web durum makinesi (fikokontrolCad.ts) ---- // Tuş bitmask'ı web ile aynı: 1=Sol, 2=Sağ, 4=Orta. // Orta(4) → PAN (harekete kadar; hareketsiz bırakış = merkezle+pivot) // Orta+Sağ(6) → DÖNDÜR // Sol+Sağ(3) → ZOOM // DÖNDÜR iken bir tuş bırakılıp Orta basılı kalırsa(4) → ZOOM'a geç (CATIA jesti) // A/S/D basılıyken sürükleme → Döndür/Zoom/Pan (web keyState eşi) const f32 lx = io.MousePos.x - vpRectMinX; const f32 ly = io.MousePos.y - vpRectMinY; fare.sonX = lx; fare.sonY = ly; // pick imleci const f32 mdx = io.MouseDelta.x, mdy = io.MouseDelta.y; const bool hareket = (mdx != 0.0f || mdy != 0.0f); const int btn = (ImGui::IsMouseDown(ImGuiMouseButton_Left) ? 1 : 0) | (ImGui::IsMouseDown(ImGuiMouseButton_Right) ? 2 : 0) | (ImGui::IsMouseDown(ImGuiMouseButton_Middle) ? 4 : 0); const bool basildi = ImGui::IsMouseClicked(ImGuiMouseButton_Left) || ImGui::IsMouseClicked(ImGuiMouseButton_Right) || ImGui::IsMouseClicked(ImGuiMouseButton_Middle); // Alan seçimi (trap) yalnız SERBEST seçim bağlamında: özel modlar aktifken // devre dışı (sketcher/yönSeç/refSecim/CAM-seçim/ölçüm tek-tık akışlarıdır). g_sonAdim = "kareCiz: kamera/secim girdisi"; // faz izleyici // CATIA: Sketcher'da da çerçeve seçimi VAR — ama yalnız SEÇİM modunda (çizim aracı // yokken) ve segment-bazlı sadece eskiz öğelerini seçer (aşağıda sketchKisitAyarla). const bool sketcherAktif = sketchNesne() && cekirdek.workbenchler().aktif() == "sketcher"; const bool sketcherSecimModu = sketcherAktif && sketchArac.empty(); const bool trapUygun = (!sketcherAktif || sketcherSecimModu) && hizliYonSecim == 0 && !refSecim.aktif() && !camSis.secim().aktif() && !(olcum.acik && olcum.kip != OlcumKip::Ayarlar); // Datum (ekran-sabit ölçekli) düğümlerin trap testi görsel boyutla eşleşsin // diye kamera mesafesi her kare alan seçime iletilir (render/pick ile aynı kaynak). alan.kameraMesafeAyarla(kamera.mesafe()); // Sketcher seçim modu → çerçeve YALNIZ aktif sketch'e (segment-bazlı); değilse 3D. alan.sketchKisitAyarla(sketcherSecimModu ? static_cast(aktifSketch) : 0u); // CATIA çerçeve seçimi köprüsü: sketcher seçim modunda alan seçimi eskiz // geometrilerini sketchSecili'ye uygular (3D sahne seçimi kirlenmez); değilse 3D. const auto alanBitirUygula = [&](const Mat4f& vp, const Vec2f& vpBoyut, TrapModifier m) -> u32 { if (alan.sketchKisit() != 0) { const std::vector geolar = alan.bitirSketch(vp, {0.0f, 0.0f}, vpBoyut); if (m == TrapModifier::Degistir) sketchSecili.clear(); for (u32 g : geolar) { const auto it = std::find(sketchSecili.begin(), sketchSecili.end(), g); if (m == TrapModifier::Cikar) { if (it != sketchSecili.end()) sketchSecili.erase(it); } else if (it == sketchSecili.end()) { sketchSecili.push_back(g); // Ekle/Degistir: biriktir (tekil) } } cekirdek.olaylar().yayinla( SketchSecimDegisti{aktifSketch, static_cast(sketchSecili.size())}, "Sketch"); return (u32)geolar.size(); } return alan.bitir(vp, {0.0f, 0.0f}, vpBoyut, m); }; // ---- Girdi jest sistemi (A): ham girdiyi besle (GÖZLEMCİ — tüketmez) ---- // guiUstunde: menü/panel/pusula/trap üstünde sahne-jestleri bastırılır. Böylece // mevcut CATIA kamera/seçim davranışı korunur; jest sistemi yalnız ek menü tetikler. { girdi::FareKare fk; fk.sol = ImGui::IsMouseDown(ImGuiMouseButton_Left); fk.orta = ImGui::IsMouseDown(ImGuiMouseButton_Middle); fk.sag = ImGui::IsMouseDown(ImGuiMouseButton_Right); fk.konum = {lx, ly}; fk.delta = {mdx, mdy}; fk.tekerlek = io.MouseWheel; fk.dt = io.DeltaTime; fk.guiUstunde = io.WantCaptureMouse || !vpHover || pusulaUstunde || alan.aktifMi(); fk.hover = hoverProbu.simdiki(); // bağlam enjeksiyonu (bu karenin hover'ı) // On-demand: 3B sahnede bir şey hover'lıyken (nokta/kenar/yüz vurgusu) sürekli // kare iste → seyrek karede highlight titremez (heartbeat re-pick flicker'ı biter). if (hoverProbu.simdiki().govdeId || sahne.vurgu.govde) kareIste(2); jestSis.guncelle(fk); } if (vpHover && !pusulaUstunde && !paftaModda && !ort.pmiDurum.fareUstunde) { if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { fare.sol = true; fare.solBasX = lx; fare.solBasY = ly; fare.solBasZaman = simdi; fare.uzunBasIslendi = false; // Trap başlat: poligon → her tık bir köşe (ilk tık modu açar). Dikdörtgen/ // fırça basışta BAŞLAMAZ — 6px sürüklenince tembel başlar (aşağıda) ki // uzun-bas (Hızlı 3D) ve normal tık seçimiyle çakışmasın. if (trapUygun && alan.poligonKipMi()) { if (!alan.aktifMi()) alan.basla(lx, ly); else alan.poligonNokta(lx, ly); } } if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { fare.sag = true; fare.sagBasX = lx; fare.sagBasY = ly; fare.sagBasZaman = simdi; if (alan.aktifMi()) alan.iptal(); // trap sürükleme/poligon sağ-tık iptal } if (ImGui::IsMouseClicked(ImGuiMouseButton_Middle)) { fare.orta = true; fare.ortaBasX = lx; fare.ortaBasY = ly; fare.ortaBasZaman = simdi; } if (basildi) fare.kameraKullandi = false; // yeni basış → seçim/kamera ayrımı sıfırla if (io.MouseWheel != 0.0f && !secimYigini.acikMi()) { // Yığın açıkken tekerlek adaylar arası gezer (overlay bloğunda) → zoom yok. Vec3f h; // imleç altındaki yüzey/gövde → zoom hedefi; boşlukta imlecDunya (focus düzlemi) const bool bulundu = pickAyar().ortaMerkezAkilli ? imlecMerkez(lx, ly, h) : imlecYuzey(lx, ly, h); kamera.imlecYakinlastir(bulundu ? h : imlecDunya(lx, ly), io.MouseWheel * 1.7f); } if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { // Poligon trap aktifse çift-tık = poligonu kapat + uygula (odaklama değil). if (alan.aktifMi() && alan.poligonKipMi() && alan.iz().size() >= 3) { const TrapModifier m = io.KeyShift ? TrapModifier::Cikar : io.KeyCtrl ? TrapModifier::Ekle : TrapModifier::Degistir; const Mat4f vp = kamera.izdusum(sonEnBoy) * kamera.gorunum(); const u32 adet = alanBitirUygula(vp, {avail.x, avail.y}, m); bilgiVer(std::to_string(adet) + " öğe seçildi (" + trapAdi(alan.tip) + ")"); } else if (Dugum* d = sahne.bul(sahne.vurgu.govde)) { // AKILLI ÇİFT-TIK (bağlam-duyarlı, profesyonel CAD — AyarSistemi bayrağı): // YÜZ kipinde çift-tık yüz → o yüzde Sketch başlat; KENAR kipinde çift-tık // kenar → teğet kenar zinciri seç. Motor HAZIR; yalnız jest köprüsü. bool akilliYapildi = false; if (dogrudanAyar().dmCiftTikAkilli && sahne.vurgu.alt != 0) { if (sahne.kip() == SecimKip::Kenar) { sahne.sec(sahne.vurgu, false); // teğet zincir tohumu = çift-tıklanan kenar const int n = akilliSecimUygula(sahne, AkilliSecim::KenarZinciri); bilgiVer(n > 0 ? (std::to_string(n) + " teğet kenar seçildi") : std::string("Teğet kenar zinciri bulunamadı")); akilliYapildi = true; } else if (sahne.kip() == SecimKip::Yuz) { sahne.sec(sahne.vurgu, false); // sketch düzlemi = çift-tıklanan yüz if (auto* o = cekirdek.menuler().bul("Sketch", "Sketch Oluştur")) cekirdek.komutCalistir(*o, "gizmo"); akilliYapildi = true; } } // Çift-tıklanan katı bir feature'ın sonuç (Ithal) gövdesi mi? → // düzenleme penceresi aç (Pad/Pocket/Shaft/Groove/Hole). Değilse // eski davranış: seç + odakla. u32 featKimlik = 0; if (!akilliYapildi) for (u32 fk : partD.featureler()) if (parca::Feature* f = partD.bul(fk); f && f->sonucDugum() == sahne.vurgu.govde) { featKimlik = fk; break; } if (akilliYapildi) { // akıllı jest işi bitirdi (sketch/zincir) — odakla/feature penceresi açma } else if (featKimlik) { featurePencereAc(featKimlik); } else { sahne.sec(sahne.vurgu, false); kamera.sigdir(d->dunyaMerkez(), d->dunyaYaricap() + 0.4f); } } else { tumunuSigdir(); } } } // ---- Merkezi navigasyon kipi: her kare tuş kombinasyonundan DETERMİNİSTİK // çöz (durum-makinesi/geçmiş YOK → "orta basılıyken sağ bas-bırak" gibi kip // sızması olmaz; bir tuş bırakılınca kombinasyon → kip geri döner). Kip // DEĞİŞİNCE olay yay: makro/plugin/Lua tek merkezden izler. Navigasyon // viewport'ta başlar; başladıysa (fare.kip>=0) fare panele kaysa bile drag sürer. { // Navigasyon viewport'ta başlar; başladıysa (fare.kip>=0) fare panele kaysa // bile drag sürer. navBaglam false → tüm tuşlar 0 → durum makinesi sıfırlanır. const bool navBaglam = !paftaModda && !ort.pmiDurum.fareUstunde && ((vpHover && !pusulaUstunde) || fare.kip >= 0); const girdi::NavKip navKip = fare.navKip.besle( navBaglam && (btn & 1), navBaglam && (btn & 4), navBaglam && (btn & 2), navBaglam && ImGui::IsMouseReleased(ImGuiMouseButton_Right)); const int yeniKip = girdi::navKipInt(navKip); if (yeniKip != fare.kip) cekirdek.olaylar().yayinla( NavKipDegisti{(u32)navKip, (u32)girdi::navKipEnum(fare.kip)}, "Nav", OlaySiddet::Iz, OlayKaynak::Kullanici); fare.kip = yeniKip; } // A/S/D klavye override (web keyState): herhangi bir tuş basılıyken geçerli. const int klavyeKip = ImGui::IsKeyDown(ImGuiKey_A) ? 0 : ImGui::IsKeyDown(ImGuiKey_S) ? 1 : ImGui::IsKeyDown(ImGuiKey_D) ? 2 : -1; const int etkinKip = (klavyeKip >= 0 && btn != 0) ? klavyeKip : fare.kip; // Aktif kipe göre kamerayı sür (delta ImGui IO'dan). if (etkinKip == 0) { kamera.dondur(mdx, mdy); if (hareket) fare.kameraKullandi = true; } else if (etkinKip == 1) { kamera.yakinlastir(-mdy * 0.03f); if (hareket) fare.kameraKullandi = true; } else if (etkinKip == 2) { kamera.kaydir(mdx, mdy, io.KeyShift); if (hareket) fare.kameraKullandi = true; } // ---- Dokunmatik / kalem navigasyonu (fare yolundan BAĞIMSIZ ikinci kaynak) ---- // Jest çözümü motor/girdi/dokunma_jest.hpp'de (saf durum makinesi, testli); burada // yalnız kamera birimine çevrilir. Parmak yoksa guncelle() erken çıkar → sıcak yol // maliyeti ~sıfır (tahsis yok, O(5) sabit tarama). { auto& dokunma = ort.dokunma; dokunma.esleme = dokunmaEslemeGlobal(); // POD kopya — ayar anında geçerli olsun dokunma.ayar = dokunmaAyarGlobal(); dokunma.guncelle(io.DeltaTime); const girdi::DokunmaCikti dc = dokunma.tuket(); // ---- Parmakla TUTAMAÇ sürükleme (R/C fillet gizmosu) ---- // Fare yolu bunu `pusulaUstunde` ile çözüyor: tutamaç üstündeyken nav bastırılır, // sol-sürükleme tutamaca gider. Parmakta sol tuş YOK → o bayrak hiç true olmuyordu // ve sürükleme orbit'e kaçıyordu. Çözüm: parmak DURGUNKEN tutamaca yakınsa "aday" // ol; kayma başlayınca nav yerine sentetik sol tuşu BASILI sür → mevcut tutamaç // kodu (IsMouseClicked + IsMouseDown) hiç değişmeden çalışır. { Vec2f dp; bool dSuruklendi = false; const bool tekVar = dokunma.tekIsaretciHam(dp, dSuruklendi); // TÜM tutamaç/gizmo'lar: hepsi `pusulaUstunde`'yi "yakın && sol tuş basılı" ile // besliyor → parmakta hiç true olmaz. Her biri ayrıca tuştan BAĞIMSIZ // `tutamacYakin` yazar; aday koşulu onların birleşimi. // ViewCube (vizyon_kupu) BURADA YOK: `ustunde`'yi hover'da (tuşsuz) zaten // set ediyor → parmakla çalışıyor, sürükleme gerektirmez (tıkla-snap). const bool gizmoYakin = (ort.kenarFillet.aktif && ort.kenarFillet.tutamacYakin) || (ort.yuzTasi.aktif && ort.yuzTasi.tutamacYakin) || (ort.pushPull.aktif && ort.pushPull.tutamacYakin) || (ort.featureReDuzenle.aktif && ort.featureReDuzenle.tutamacYakin); if (tekVar) { if (ort.dokunmaIsaretci == 0 && !dSuruklendi && gizmoYakin) ort.dokunmaIsaretci = 1; // aday: tutamaç altında bekliyor if (ort.dokunmaIsaretci == 1 && dSuruklendi) { io.AddMouseButtonEvent(ImGuiMouseButton_Left, true); // sürükleme başladı ort.dokunmaIsaretci = 2; } // Sürüklerken de imleci parmağa taşı (tekIsaretciKonum sürüklemede susar). if (ort.dokunmaIsaretci) io.AddMousePosEvent(dp.x, dp.y); } else if (ort.dokunmaIsaretci) { if (ort.dokunmaIsaretci == 2) io.AddMouseButtonEvent(ImGuiMouseButton_Left, false); ort.dokunmaIsaretci = 0; // parmak kalktı → tuşu bırak } } // İşaretçi kipindeyken nav UYGULANMAZ (parmak tutamacı sürüyor, kamerayı değil). if (ort.dokunmaIsaretci == 0) { if (dc.dondurX != 0.0f || dc.dondurY != 0.0f) { kamera.dondur(dc.dondurX, dc.dondurY); fare.kameraKullandi = true; } if (dc.panX != 0.0f || dc.panY != 0.0f) { kamera.kaydir(dc.panX, dc.panY, io.KeyShift); fare.kameraKullandi = true; } if (dc.zoomLogOran != 0.0f) { // yakinlastir(adim): mesafe *= zoomOran^adim (zoomOran<1). Pinch ln-oranını // adıma çevir → parmak arası 2× açılınca mesafe tam yarıya iner (ekran-uzayı // birebir his), zoomOran ayarından bağımsız. const f32 lnOran = std::log(kamera.zoomOran()); if (lnOran < -1e-6f) kamera.yakinlastir(-dc.zoomLogOran / lnOran); fare.kameraKullandi = true; } } // if (ort.dokunmaIsaretci == 0) — tutamaç sürülürken nav yok // ---- Farkındalık: dokunmatik nav da NavKipDegisti yayar ---- // Fare yolu bu olayı zaten yayınlıyordu (~4316). Dokunmatik sessiz kalsaydı // makro/plugin/Lua "kamera döndürülüyor"u YALNIZ fareyle görürdü. Artık nav // dinleyicisi girdi kaynağını BİLMEZ — tek olay, her girdi. { const int dkip = (dc.dondurX != 0.0f || dc.dondurY != 0.0f) ? 0 : (dc.zoomLogOran != 0.0f) ? 1 : (dc.panX != 0.0f || dc.panY != 0.0f) ? 2 : -1; if (dkip != ort.dokunmaNavKip) { cekirdek.olaylar().yayinla( NavKipDegisti{(u32)girdi::navKipEnum(dkip), (u32)girdi::navKipEnum(ort.dokunmaNavKip)}, "Nav", OlaySiddet::Iz, OlayKaynak::Kullanici); ort.dokunmaNavKip = dkip; } } // ---- Farkındalık: dokunma jesti olayı (FareJestiOldu ikizi) ---- if (dc.tik || dc.ciftTik || dc.uzunBas) cekirdek.olaylar().yayinla( DokunmaJestiOldu{dc.uzunBas ? 2u : (dc.ciftTik ? 1u : 0u), (u32)dokunma.aktifSayi(), dc.olayKalem, dc.olayGovdeTusu}, "Dokunma", OlaySiddet::Iz, OlayKaynak::Kullanici); // ---- Parmakla seçim (dokun = tıkla, dokun-sürükle = döndür) ---- // Dokunuş TÜKETİLDİĞİ için Windows fare mesajı üretmez → ImGui imleci son fare // yerinde kalırdı. Hareketsiz parmağın altına imleci taşı: pick/hover dokunulan // yeri hedefler. Sürükleme başlayınca tekIsaretciKonum false döner → orbit // sırasında pick parmağı kovalamaz. Vec2f dokunmaNokta; if (dokunma.tekIsaretciKonum(dokunmaNokta)) io.AddMousePosEvent(dokunmaNokta.x, dokunmaNokta.y); // Jest "tık" derse ImGui'ye GERÇEK sol-tık üret → mevcut dev seçim zinciri // (sketcher/referans/akıllı seçim/selection stack/ölçüm) BİREBİR çalışır, tek // satırı kopyalanmaz. down ve up AYRI karelerde: aynı karede ikisi olursa ImGui // kenar üretmez (IsMouseClicked/IsMouseReleased kare geçişine bakar) → zincir // tıklamayı hiç görmez. if (ort.dokunmaTikAsama == 2) { io.AddMouseButtonEvent(ort.dokunmaTikDugme, true); ort.dokunmaTikAsama = 1; } else if (ort.dokunmaTikAsama == 1) { io.AddMouseButtonEvent(ort.dokunmaTikDugme, false); ort.dokunmaTikAsama = 0; } // ciftTik de sentetik tık üretir: ardışık iki tık ImGui'nin KENDİ çift-tık // algılamasını doğurur → çift-dokunuş "odakla/feature düzenle" yolu (mevcut // IsMouseDoubleClicked zinciri) kendiliğinden çalışır. if (dc.tik || dc.ciftTik) { io.AddMousePosEvent(dc.olayNokta.x, dc.olayNokta.y); // Kalem GÖVDE (barrel) tuşuyla dokunuş = SAĞ tık (kalemin yerleşik kuralı) → // bağlam menüsü. Parmak/kalem-ucu dokunuşu = sol tık (seçim). ort.dokunmaTikDugme = dc.olayGovdeTusu ? ImGuiMouseButton_Right : ImGuiMouseButton_Left; ort.dokunmaTikAsama = 2; } else if (dc.uzunBas) { // Uzun bas → SAĞ tık: bağlam menüsü (~4912) hareketsiz sağ-bırakmada açılır. // Sentetik down/up 1 kare arayla → kayma=0, süre≈16ms → menünün "kayma<6px, // süre<0.5s" koşulu geçer. Sağ tuş TEK başına nav kipi üretmez (nav_kip.hpp: // sağ yalnız sol/orta ile kombinasyonda anlamlı) → kamera etkilenmez. // Parmak hâlâ basılı: jest motoru uzunYayildi ile hem navı hem bırakmadaki // tık'ı bastırır → menü açıkken model dönmez, arkada seçim olmaz. io.AddMousePosEvent(dc.olayNokta.x, dc.olayNokta.y); ort.dokunmaTikDugme = ImGuiMouseButton_Right; ort.dokunmaTikAsama = 2; } } // Trap TEMBEL başlatma: sol basılı + 6px kayma → dikdörtgen/fırça o zaman // başlar (çapa = basış noktası). Hareketsiz basılı tutma uzun-bas'a kalır; // uzun-bas tetiklendiyse veya kamera kullanıldıysa trap açılmaz. if (fare.sol && trapUygun && !alan.aktifMi() && !alan.poligonKipMi() && !fare.uzunBasIslendi && !fare.kameraKullandi) { const f32 kayma = std::abs(lx - fare.solBasX) + std::abs(ly - fare.solBasY); if (kayma >= 6.0f) alan.basla(fare.solBasX, fare.solBasY); } // Trap sürükleme: dikdörtgen köşe / fırça izi noktası biriktir (poligon // sürüklemez, tıkla köşe ekler). Sol-drag kamerayı sürmez → çakışma yok. if (alan.aktifMi() && !alan.poligonKipMi() && hareket) alan.guncelle(lx, ly); // Uzun basış → "Hızlı 3D" ağırlık/kütük widget'ı (sol, hareketsiz, kamera yok) // Trap sürüklemesi aktifken long-press'i atla (dikdörtgen çizimiyle çakışmasın). if (fare.sol && !fare.uzunBasIslendi && !fare.kameraKullandi && !alan.aktifMi()) { const f32 kayma = std::abs(lx - fare.solBasX) + std::abs(ly - fare.solBasY); const auto sure = std::chrono::duration(simdi - fare.solBasZaman).count(); if (sure > 0.55f && kayma < 6.0f && sahne.vurgu.govde) { fare.uzunBasIslendi = true; // Katı/mesh'li gövde → standart ağırlık/kütük widget. Eksen/EksenSistemi → // tip-özel Hızlı 3D widget (registry; hizli3d_penceresi.cpp). Diğer mesh'siz // datum/wireframe (düzlem/Tel/Sketch) → açma (kütük NaN/çökme koruması). const Dugum* ubd = sahne.bul(sahne.vurgu.govde); const bool ozelTip = ubd && (ubd->tip == DugumTip::Eksen || ubd->tip == DugumTip::EksenSistemi); if (ubd && (ubd->indeksSayi > 0 || ozelTip)) { hizli3dKimlik = sahne.vurgu.govde; hizli3dAcik = true; hizliDurum.hizli3dAcilisX = io.MousePos.x; hizliDurum.hizli3dAcilisY = io.MousePos.y; } } } // Bırakma aksiyonları bool trapUygulandi = false; if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) { // Trap (dikdörtgen/iz) sürüklemesi: gerçek sürükleme ise adayları uygula, // mikro-sürükleme ise iptal edip normal tık seçimine düş. Poligon burada // bitmez (çift-tık/Enter kapatır). if (alan.aktifMi() && !alan.poligonKipMi()) { const f32 kayma = std::abs(lx - fare.solBasX) + std::abs(ly - fare.solBasY); if (kayma >= 6.0f) { const TrapModifier m = io.KeyShift ? TrapModifier::Cikar : io.KeyCtrl ? TrapModifier::Ekle : TrapModifier::Degistir; const Mat4f vp = kamera.izdusum(sonEnBoy) * kamera.gorunum(); const u32 adet = alanBitirUygula(vp, {avail.x, avail.y}, m); bilgiVer(std::to_string(adet) + " öğe seçildi (" + trapAdi(alan.tip) + ")"); trapUygulandi = true; // normal tık seçim zincirini atla } else { alan.iptal(); // mikro-sürükleme → normal tık } } if (!trapUygulandi && fare.sol && !fare.uzunBasIslendi && !fare.kameraKullandi) { const f32 kayma = std::abs(lx - fare.solBasX) + std::abs(ly - fare.solBasY); const auto sure = std::chrono::duration(simdi - fare.solBasZaman).count(); // ---- Alt + Sol Tık: Selection Stack (CATIA "Other Selection") ---- // İmleç ışınının kestiği üst üste tüm adayları topla + yığın penceresi aç. // Shift+Alt teğet-yüz seçimine ayrıldı (aşağıda) → yığın yalnız düz Alt'ta açılır. if (io.KeyAlt && !io.KeyShift && kayma < 6.0f && sure < 0.45f) { const Mat4f vpM = kamera.izdusum(sonEnBoy) * kamera.gorunum(); const f32 ndcX = 2.0f * lx / avail.x - 1.0f; const f32 ndcY = 2.0f * ly / avail.y - 1.0f; const Mat4f invVP = vpM.ters(); const Vec4f yH = invVP * Vec4f{ndcX, ndcY, 1.0f, 1.0f}; // reverse-Z: yakın = 1 const Vec4f uH = invVP * Vec4f{ndcX, ndcY, 0.0f, 1.0f}; // reverse-Z: uzak = 0 const Vec3f yakin = yH.xyz() * (1.0f / yH.w); const Vec3f uzak = uH.xyz() * (1.0f / uH.w); secimYigini.ac(sahne, yakin, (uzak - yakin).birim(), vpM, ImVec2(vpRectMinX, vpRectMinY), ImVec2(avail.x, avail.y), io.MousePos, secimSis, refSecim.aktif() && refSecim.beklenenTur() >= 1 ? refSecim.beklenenTur() : -1); if (secimYigini.acikMi()) secimSis.hoverBastir(true); trapUygulandi = true; // normal seçim zincirini atla } else if (kayma < 6.0f && sure < 0.45f) { SketchNesnesi* skTik = sketchNesne(); // Sketcher tıklaması: araç aktifse uv noktası koy / geometri tamamla, // araç yoksa imleç-altı eskiz geometrisini seçime ekle/çıkar (kısıt için). if (skTik && cekirdek.workbenchler().aktif() == "sketcher") { Dugum* dz = sahne.bul(skTik->duzlem()); eskiz::EskizVeri& ev = skTik->eskiz(); f64 u, v; const bool duzenMod = sketchDuzenMi(sketchArac); if (duzenMod && dz && ekranUV(dz, lx, ly, u, v)) { // ---- 2B Düzenleme tık akışı (araç bazlı çoklu tık) ---- if (sonSnap.tip != eskiz::SnapTip::Yok) { u = sonSnap.u; v = sonSnap.v; } if (sketchArac == "trim" || sketchArac == "uzat" || sketchArac == "kapat" || sketchArac == "hizlikirp" || sketchArac == "tumleyen") { // Tek tık: imleç-altı geometri → işlem. Araç aktif kalır (continue). const u32 g = sketchEnYakin(ev, u, v); if (g) { bool ok = sketchArac == "trim" ? ev.trim(g, u, v) : sketchArac == "uzat" ? ev.uzat(g, u, v) : sketchArac == "hizlikirp" ? ev.hizliKirp(g, u, v, eskiz::kirpModDon(sketchDurum.kirpMod)) : sketchArac == "tumleyen" ? ev.tumleyen(g) != 0 : ev.kapat(g); if (ok) { ev.coz(); bilgiVer("Uygulandı — devam edebilirsiniz"); } else bilgiVer("İşlem uygulanamadı (kesişim/uygun uç yok)"); } else bilgiVer("İmleç altında geometri yok"); } else if (sketchArac == "bol") { const u32 g = sketchEnYakin(ev, u, v); if (g) { if (ev.bol(g, u, v)) { ev.coz(); bilgiVer("Bölündü"); } else bilgiVer("Bölünemedi"); } else bilgiVer("İmleç altında geometri yok"); } else if (sketchArac == "fillet" || sketchArac == "pah") { // CATIA tarzı: 2 çizgi seç → fareyi oynat (canlı önizleme) → TIKLA yerleştir. // Değer kutusu doluysa onu, boşsa imleç konumundan türetilen yarıçap/mesafe. if (sketchSecili.size() < 2) { const u32 g = sketchEnYakin(ev, u, v); if (g) { auto it = std::find(sketchSecili.begin(), sketchSecili.end(), g); if (it == sketchSecili.end()) sketchSecili.push_back(g); if (sketchSecili.size() >= 2) { sketchDuzenFaz = 1; bilgiVer("Fareyi oynat → yarıçap/mesafe, sonra tıkla"); } else bilgiVer("1 çizgi — 2. çizgiyi seçin"); } } else { // Faz 1: tıklanan konum boyutu belirler → uygula (araç aktif kalır). const eskiz::Geometri* gA = ev.geoBul(sketchSecili[0]); const eskiz::Geometri* gB = ev.geoBul(sketchSecili[1]); f64 val = sketchDuzGiris[0][0] ? std::strtod(sketchDuzGiris[0], nullptr) : 0.0; if (val <= 1e-9 && gA && gB && gA->tip == eskiz::GeoTip::Cizgi && gB->tip == eskiz::GeoTip::Cizgi) { const f64 A1x = ev.noktaX(gA->kimlik, 0), A1y = ev.noktaY(gA->kimlik, 0); const f64 A2x = ev.noktaX(gA->kimlik, 1), A2y = ev.noktaY(gA->kimlik, 1); const f64 B1x = ev.noktaX(gB->kimlik, 0), B1y = ev.noktaY(gB->kimlik, 0); const f64 B2x = ev.noktaX(gB->kimlik, 1), B2y = ev.noktaY(gB->kimlik, 1); const f64 rx = A2x - A1x, ry = A2y - A1y, sx = B2x - B1x, sy = B2y - B1y; const f64 payda = rx * sy - ry * sx; if (std::abs(payda) > 1e-9) { // köşe = iki doğrunun kesişimi const f64 tt = ((B1x - A1x) * sy - (B1y - A1y) * sx) / payda; const f64 kx = A1x + rx * tt, ky = A1y + ry * tt; const auto yon = [&](f64 p1x, f64 p1y, f64 p2x, f64 p2y, f64& ux, f64& uy) { const f64 d1 = std::hypot(p1x - kx, p1y - ky); const f64 d2 = std::hypot(p2x - kx, p2y - ky); const f64 ux0 = (d1 > d2 ? p1x : p2x) - kx, uy0 = (d1 > d2 ? p1y : p2y) - ky; const f64 L = std::hypot(ux0, uy0); ux = L > 1e-9 ? ux0 / L : 0; uy = L > 1e-9 ? uy0 / L : 0; }; f64 uAx, uAy, uBx, uBy; yon(A1x, A1y, A2x, A2y, uAx, uAy); yon(B1x, B1y, B2x, B2y, uBx, uBy); const f64 pu = u - kx, pv = v - ky; val = std::max(std::abs(pu * uAx + pv * uAy), std::abs(pu * uBx + pv * uBy)); } } if (val < 1e-6) val = sketchDurum.sketchFilletYedekYari; // yedek (AyarSistemi: sketcher.fillet_yedek_yari) const auto trimMod = eskiz::trimModDon(sketchDurum.filletTrimMod); u32 yeni = 0; if (sketchArac == "fillet") { yeni = ev.koseYuvarlat(sketchSecili[0], sketchSecili[1], val, trimMod); } else { // Pah: kutu(lar) doluysa ölçü moduna göre iki kenar setback'i; // boşsa imleçten türetilen simetrik mesafe (val, val). f64 mA = val, mB = val; if (sketchDuzGiris[0][0]) { const f64 v0 = std::strtod(sketchDuzGiris[0], nullptr); const f64 v1 = sketchDuzGiris[1][0] ? std::strtod(sketchDuzGiris[1], nullptr) : (sketchDurum.pahOlcuMod == 1 ? v0 : 45.0); pahMesafeHesapla(sketchDurum.pahOlcuMod, v0, v1, mA, mB); } yeni = ev.pah(sketchSecili[0], sketchSecili[1], mA, mB, trimMod); } ev.coz(); sketchSecili.clear(); sketchDuzenFaz = 0; for (auto& s : sketchDuzGiris) s[0] = 0; bilgiVer(yeni ? "Uygulandı — 2 çizgi daha seçebilirsiniz" : "Uygulanamadı (kesişim yok)"); } } else if (sketchArac == "aciortay" || sketchArac == "tegetcizgi" || sketchArac == "kesisimnokta" || sketchArac == "izdusumnokta") { // Seç-seç-uygula (CATIA Bisecting/Bi-Tangent Line + Intersection/ // Projection Point): 2. öğe seçilince hemen uygulanır, araç aktif kalır. if (sketchSecili.size() < 2) { const u32 g = sketchEnYakin(ev, u, v); if (g && std::find(sketchSecili.begin(), sketchSecili.end(), g) == sketchSecili.end()) sketchSecili.push_back(g); } if (sketchSecili.size() >= 2) { const eskiz::Geometri* gA = ev.geoBul(sketchSecili[0]); const eskiz::Geometri* gB = ev.geoBul(sketchSecili[1]); const auto cemberMi = [](const eskiz::Geometri* g) { return g && (g->tip == eskiz::GeoTip::Daire || g->tip == eskiz::GeoTip::Yay); }; bool ok = false; std::string mesaj; if (sketchArac == "aciortay") { // 2 çizgi → açıortay YAPI çizgisi (paralelde ortadan geçen doğru). if (gA && gB && gA->tip == eskiz::GeoTip::Cizgi && gB->tip == eskiz::GeoTip::Cizgi) { const f64 A1x = ev.noktaX(gA->kimlik, 0), A1y = ev.noktaY(gA->kimlik, 0); const f64 A2x = ev.noktaX(gA->kimlik, 1), A2y = ev.noktaY(gA->kimlik, 1); const f64 B1x = ev.noktaX(gB->kimlik, 0), B1y = ev.noktaY(gB->kimlik, 0); const f64 B2x = ev.noktaX(gB->kimlik, 1), B2y = ev.noktaY(gB->kimlik, 1); const f64 rx = A2x - A1x, ry = A2y - A1y, sx = B2x - B1x, sy = B2y - B1y; const f64 payda = rx * sy - ry * sx; f64 kx, ky, ux, uy; // taban noktası + açıortay birim yönü bool gecerli = true; if (std::abs(payda) < 1e-9) { // Paralel çizgiler: açıortay = iki çizgi ortasından geçen paralel. kx = (A1x + A2x + B1x + B2x) * 0.25; ky = (A1y + A2y + B1y + B2y) * 0.25; const f64 L = std::hypot(rx, ry); gecerli = L > 1e-9; ux = gecerli ? rx / L : 0; uy = gecerli ? ry / L : 0; } else { const f64 tt = ((B1x - A1x) * sy - (B1y - A1y) * sx) / payda; kx = A1x + rx * tt; ky = A1y + ry * tt; // köşe (kesişim) // Köşeden uzak uçlara birim yönler → toplamın birimi = açıortay // (fillet köşe/yön deseni ile aynı). const auto yon = [&](f64 p1x, f64 p1y, f64 p2x, f64 p2y, f64& vx, f64& vy) { const f64 d1 = std::hypot(p1x - kx, p1y - ky); const f64 d2 = std::hypot(p2x - kx, p2y - ky); const f64 vx0 = (d1 > d2 ? p1x : p2x) - kx; const f64 vy0 = (d1 > d2 ? p1y : p2y) - ky; const f64 L = std::hypot(vx0, vy0); vx = L > 1e-9 ? vx0 / L : 0; vy = L > 1e-9 ? vy0 / L : 0; }; f64 uAx, uAy, uBx, uBy; yon(A1x, A1y, A2x, A2y, uAx, uAy); yon(B1x, B1y, B2x, B2y, uBx, uBy); ux = uAx + uBx; uy = uAy + uBy; const f64 L = std::hypot(ux, uy); if (L > 1e-9) { ux /= L; uy /= L; } else { ux = -uAy; uy = uAx; } // zıt yönler (düz açı) → dik açıortay } if (gecerli) { constexpr f64 SL = 1e4; // sonsuz çizgi ile aynı pragmatik uzunluk const u32 yeni = ev.cizgiEkle(kx - ux * SL, ky - uy * SL, kx + ux * SL, ky + uy * SL); ev.yapiAyarla(yeni, true); ok = true; mesaj = "Açıortay eklendi (yapı)"; } else mesaj = "Çizgi yönü sıfır"; } else mesaj = "Açıortay: 2 çizgi seçin"; } else if (sketchArac == "tegetcizgi") { // 2 daire/yay → ortak DIŞ teğet çizgi (tıka yakın aday) + Teğet kısıt. if (cemberMi(gA) && cemberMi(gB)) { const f64 C1x = ev.noktaX(gA->kimlik, 2), C1y = ev.noktaY(gA->kimlik, 2); const f64 C2x = ev.noktaX(gB->kimlik, 2), C2y = ev.noktaY(gB->kimlik, 2); const f64 r1 = ev.px(gA->ir), r2 = ev.px(gB->ir); const f64 dx = C2x - C1x, dy = C2y - C1y, d = std::hypot(dx, dy); if (d > 1e-9 && std::abs(r1 - r2) <= d) { const f64 alfa = std::atan2(dy, dx); const f64 beta = std::acos(std::clamp((r1 - r2) / d, -1.0, 1.0)); // İki dış teğet adayından son tık tarafına yakın olanı seç. f64 enIyi = 1e30, P1x = 0, P1y = 0, P2x = 0, P2y = 0; for (const f64 isaret : {1.0, -1.0}) { const f64 aa = alfa + isaret * beta; const f64 nx = std::cos(aa), ny = std::sin(aa); const f64 q1x = C1x + r1 * nx, q1y = C1y + r1 * ny; const f64 q2x = C2x + r2 * nx, q2y = C2y + r2 * ny; const f64 md = std::hypot((q1x + q2x) * 0.5 - u, (q1y + q2y) * 0.5 - v); if (md < enIyi) { enIyi = md; P1x = q1x; P1y = q1y; P2x = q2x; P2y = q2y; } } const u32 yeni = ev.cizgiEkle(P1x, P1y, P2x, P2y); // Parametrik: iki daireye Teğet kısıtı (slotEkle deseni) → // daireler sürüklenince çizgi teğet kalır. ev.kisitEkle(eskiz::KisitTip::Tegel, {yeni, gA->kimlik}); ev.kisitEkle(eskiz::KisitTip::Tegel, {yeni, gB->kimlik}); ok = true; mesaj = "Teğet çizgi eklendi"; } else mesaj = "Dış teğet yok (iç içe / eş merkez)"; } else mesaj = "Teğet çizgi: 2 daire/yay seçin"; } else if (sketchArac == "kesisimnokta") { // Kesişim matematiği TEK OTORİTE = motor (EskizVeri::kesisimNoktalari). std::vector> ks; if (ev.kesisimNoktalari(sketchSecili[0], sketchSecili[1], ks)) { for (const auto& p : ks) ev.noktaEkle(p[0], p[1]); ok = true; mesaj = std::to_string(ks.size()) + " kesişim noktası eklendi"; } else mesaj = "Kesişim yok"; } else { // izdusumnokta: nokta + çizgi/daire → dik izdüşüm noktası const eskiz::Geometri* gN = gA && gA->tip == eskiz::GeoTip::Nokta ? gA : gB && gB->tip == eskiz::GeoTip::Nokta ? gB : nullptr; const eskiz::Geometri* gH = gN == gA ? gB : gA; if (gN && gH && (gH->tip == eskiz::GeoTip::Cizgi || cemberMi(gH))) { const f64 Px = ev.noktaX(gN->kimlik, 0), Py = ev.noktaY(gN->kimlik, 0); f64 qx = 0, qy = 0; bool var = false; if (gH->tip == eskiz::GeoTip::Cizgi) { // Sonsuz doğruya dik ayak (CATIA taşıyıcıya izdüşürür). const f64 ax = ev.noktaX(gH->kimlik, 0), ay = ev.noktaY(gH->kimlik, 0); const f64 bx = ev.noktaX(gH->kimlik, 1), by = ev.noktaY(gH->kimlik, 1); const f64 dx = bx - ax, dy = by - ay, L2 = dx * dx + dy * dy; if (L2 > 1e-12) { const f64 t = ((Px - ax) * dx + (Py - ay) * dy) / L2; qx = ax + t * dx; qy = ay + t * dy; var = true; } } else { // daire/yay: merkezden noktaya ışın ∩ çember const f64 cx = ev.noktaX(gH->kimlik, 2), cy = ev.noktaY(gH->kimlik, 2); const f64 d = std::hypot(Px - cx, Py - cy); if (d > 1e-9) { const f64 r = ev.px(gH->ir); qx = cx + (Px - cx) / d * r; qy = cy + (Py - cy) / d * r; var = true; } } if (var) { ev.noktaEkle(qx, qy); ok = true; mesaj = "İzdüşüm noktası eklendi"; } else mesaj = "İzdüşüm hesaplanamadı (çakışık nokta)"; } else mesaj = "İzdüşüm: 1 nokta + 1 çizgi/daire seçin"; } if (ok) ev.coz(); sketchSecili.clear(); bilgiVer(mesaj); } else if (sketchSecili.size() == 1) { bilgiVer("1. öğe seçildi — 2. öğeyi seçin"); } } else if (sketchArac == "ayna" || sketchArac == "simetri") { // Faz 0: geometri seç (çoklu). Faz 1: eksen çizgi seç → uygula. const u32 g = sketchEnYakin(ev, u, v); if (sketchDuzenFaz == 0) { if (g) { auto it = std::find(sketchSecili.begin(), sketchSecili.end(), g); if (it != sketchSecili.end()) sketchSecili.erase(it); else sketchSecili.push_back(g); // İlk eleman seçilince otomatik faz 1'e geç (klavye beklemeden); // ek eleman gerekiyorsa Ctrl+tık ile seçime dön. if (!io.KeyCtrl) sketchDuzenIlerlet(); else bilgiVer(std::to_string(sketchSecili.size()) + " eleman — Ctrl'süz tıkla: eksen seç"); } } else if (g) { // eksen seçimi const bool kopya = sketchDurum.sketchAynaKopya; if (kopya) ev.aynala(sketchSecili, g); else ev.simetriTasi(sketchSecili, g); ev.coz(); sketchSecili.clear(); sketchDuzenFaz = 0; sketchArac.clear(); bilgiVer(kopya ? "Aynalandı (kopya)" : "Simetri taşındı"); } } else if (sketchArac == "otele" || sketchArac == "dondur" || sketchArac == "olcekle" || sketchArac == "ofset") { // Faz 0: geometri seç. Faz 1: referans nokta al → değer penceresi. if (sketchDuzenFaz == 0) { const u32 g = sketchEnYakin(ev, u, v); if (g) { auto it = std::find(sketchSecili.begin(), sketchSecili.end(), g); if (it != sketchSecili.end()) sketchSecili.erase(it); else sketchSecili.push_back(g); // Ctrl'süz ilk seçimde otomatik faz 1 (klavye beklemez); Ctrl = çoklu seç. if (!io.KeyCtrl) sketchDuzenIlerlet(); else bilgiVer(std::to_string(sketchSecili.size()) + " eleman seçildi (Ctrl)"); } } else { // referans nokta tıklandı → değer girişi açılır sketchDuzenRef = {u, v}; bilgiVer("Referans alındı — değer girin (Enter)"); } } } else if (!sketchArac.empty() && !duzenMod && dz && ekranUV(dz, lx, ly, u, v)) { // SmartPick: overlay'de bu kare çözülen snaplı noktayı kullan // (ham fare yerine uç/merkez/izgara vb. yakalanmış nokta). if (sonSnap.tip != eskiz::SnapTip::Yok) { u = sonSnap.u; v = sonSnap.v; } sketchNoktalar.push_back({u, v}); if (sketchNoktalar.size() >= sketchAracTikSayi()) sketchAracTamamla(ev); // Spline değişken-tık: çift-tık bitirir (≥2 nokta; çift-tıkın duplicate // son noktası Tamamla'da ayıklanır). ESC zaten aracı bırakır. else if ((sketchArac == "spline" || sketchArac == "nurbsEgri") && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && sketchNoktalar.size() >= 2) sketchAracTamamla(ev); } else if (sketchArac.empty() && dz && ekranUV(dz, lx, ly, u, v)) { if (sketchDurum.sketchSurukleTuttu) { // Bu bırakma az önce bir NURBS/spline kontrol noktası sürüklemesiydi // — normal geometri/kısıt seçim toggle'ına düşülmez (aksi halde her // sürüklemeden sonra istenmeyen seçim/deseçim olurdu). sketchDurum.sketchSurukleTuttu = false; } else { // (a) Önce kısıt sembolü hit-test (overlay ekran-uzayı kutuları doldurdu). // Simgeye tıklanırsa geometri seçimine düşülmez → sil'e hazır seçim. u32 kisitVur = 0; for (const auto& hk : sketchDurum.kisitHitKutulari) if (io.MousePos.x >= hk.x0 && io.MousePos.x <= hk.x1 && io.MousePos.y >= hk.y0 && io.MousePos.y <= hk.y1) { kisitVur = hk.kimlik; break; } if (kisitVur) { if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { // ÇİFT TIK boyut → değer düzenleme kutusu (CATIA). Seçim toggle'ına // düşülmez (ikinci tık seçimi geri almasın); doğrulama pencerede // (boyutsal değilse açılmaz). sketch_ui: sketchKisitDegerPenceresiCiz. sketchDurum.kisitDuzenle = kisitVur; sketchDurum.kisitDuzenleIlk = true; sketchSeciliKisit.clear(); sketchSeciliKisit.push_back(kisitVur); } else { auto it = std::find(sketchSeciliKisit.begin(), sketchSeciliKisit.end(), kisitVur); if (it != sketchSeciliKisit.end()) sketchSeciliKisit.erase(it); else { if (!io.KeyCtrl) { sketchSeciliKisit.clear(); sketchSecili.clear(); } sketchSeciliKisit.push_back(kisitVur); } } } else { // (b) En yakın geometri (uv-uzayı mesafesi) → seçim toggle. const u32 enYakin = sketchEnYakin(ev, u, v); if (enYakin) { if (!io.KeyCtrl) sketchSeciliKisit.clear(); // geometri seçimi kısıt seçimini bırakır auto it = std::find(sketchSecili.begin(), sketchSecili.end(), enYakin); if (it != sketchSecili.end()) sketchSecili.erase(it); else { if (!io.KeyCtrl) sketchSecili.clear(); sketchSecili.push_back(enYakin); } sketchSecimYay(); // K5: seçim değişti → olay } else if (!io.KeyCtrl) { sketchSeciliKisit.clear(); if (!sketchSecili.empty()) { sketchSecili.clear(); sketchSecimYay(); } } } } } } else // Kütük "Yön Seç" modu: tıklanan yüzün normali kütük eksenine olur (çift yön mümkün) if (hizliYonSecim > 0) { Sahne::IsinVurus vr; if (imlecVurus(lx, ly, vr) && vr.govde && vr.normal.uzunlukKare() > 1e-8f) { const Vec3f n = vr.normal.birim(); if (hizliYonSecim == 1) { hizliYon1 = n; hizliYon1Var = true; hizliKutukMod = 2; } else { hizliYon2 = n; hizliYon2Var = true; } hizliYonSecim = 0; bilgiVer("Kütük yönü seçildi"); } } else // IJK hedef seçimi (Kafa penceresi "🖱 3d Seç" butonu): sıradaki // tık, düz kenarsa DOĞRULTUSUNU, yoksa yüzeyin NORMALİNİ bglm.bekleyenIjkHedef'e // yazar (Kafa penceresi kafaGuncelle karesinde tüketir; bkz. uygulama_baglam.hpp). // Yalnız BAŞARILI yakalamada mod kapanır (miss → tekrar denenebilir, hizliYonSecim ile AYNI davranış). if (bglm.ijkSecimBekliyor) { Vec3d yon; bool bulundu = false; if (sahne.kip() == SecimKip::Kenar && sahne.vurgu.govde && sahne.vurgu.alt) { if (Dugum* dd = sahne.bul(sahne.vurgu.govde); dd && dd->brep) { geometri::KenarBilgi kb; if (dd->brep->kenarBilgi(sahne.vurgu.alt, kb) && kb.dogruMu) { yon = dd->donusum.yonelim.dondur(kb.yon).birim(); bulundu = true; } } } if (!bulundu) { Sahne::IsinVurus vr; if (imlecVurus(lx, ly, vr) && vr.govde && vr.normal.uzunlukKare() > 1e-8f) { const Vec3f n = vr.normal.birim(); yon = Vec3d{(f64)n.x, (f64)n.y, (f64)n.z}; bulundu = true; } } if (bulundu) { bglm.bekleyenIjkHedef = yon; bglm.ijkSecimBekliyor = false; // 3d Seç butonu armlanırken diğer süreçlere de "ijk.mod_ac" // yayınlanmıştı (bkz. kafa_penceresi.cpp) — yakalama BURADA (yerel pencerede) // olduğundan onları silahsızlandır (aksi hâlde askıda kalırlar). g_surecKopru.hepsineGonder("ijk.mod_kapat"); bilgiVer("Yön Kafa penceresinin IJK hedefine gönderildi"); } else { bilgiVer("Boşluk — yüz veya düz kenara tıklayın (İptal: pencere üstü buton)"); } } else // ── SÜREÇLER-ARASI IJK HEDEF SEÇİMİ: yerel Kafa penceresi armlanmamış // (ijkSecimBekliyor=false) ama BAŞKA bir fikocad.exe süreci "ijk.mod_ac" // yayınlayarak bizden yön beklemiş (ijkUzakKaynakPid != 0). Aynı hit-test, // ama sonuç yerel bglm'e değil IPC ile kaynak sürece yollanır — o süreç // kendi drain'inde "ijk.hedef" olarak alır (bkz. IPC bloğu, kareCiz üstü). if (bglm.ijkUzakKaynakPid) { Vec3d yon; bool bulundu = false; if (sahne.kip() == SecimKip::Kenar && sahne.vurgu.govde && sahne.vurgu.alt) { if (Dugum* dd = sahne.bul(sahne.vurgu.govde); dd && dd->brep) { geometri::KenarBilgi kb; if (dd->brep->kenarBilgi(sahne.vurgu.alt, kb) && kb.dogruMu) { yon = dd->donusum.yonelim.dondur(kb.yon).birim(); bulundu = true; } } } if (!bulundu) { Sahne::IsinVurus vr; if (imlecVurus(lx, ly, vr) && vr.govde && vr.normal.uzunlukKare() > 1e-8f) { const Vec3f n = vr.normal.birim(); yon = Vec3d{(f64)n.x, (f64)n.y, (f64)n.z}; bulundu = true; } } if (bulundu) { char ibuf[32], jbuf[32], kbuf[32]; snprintf(ibuf, sizeof(ibuf), "%.9f", yon.x); snprintf(jbuf, sizeof(jbuf), "%.9f", yon.y); snprintf(kbuf, sizeof(kbuf), "%.9f", yon.z); g_surecKopru.gonder(bglm.ijkUzakKaynakPid, "ijk.hedef", {{"i", ibuf}, {"j", jbuf}, {"k", kbuf}}); g_surecKopru.hepsineGonder("ijk.mod_kapat"); // tek-seferlik: herkesi silahsızlandır bglm.ijkUzakKaynakPid = 0; bilgiVer("Yön diğer sürece gönderildi"); } else { bilgiVer("Boşluk — yüz veya düz kenara tıklayın (İptal: diğer süreçteki pencere üstü buton)"); } } else // GSD wireframe / Hole "sahneden nokta/yüz seç" modu aktifse: tık → // dünya noktası (+yüz normali +gövde) yakalanır, feature param'a yazılır. if (refSecim.aktif()) { SahneRefSecim::Vurus vr{}; if (refSecim.serbestNoktaMi()) { // Serbest yüzey noktası: ışın vuruşu (nokta + normal + gövde). Sahne::IsinVurus iv; if (imlecVurus(lx, ly, iv) && iv.govde) { vr.nokta = {iv.nokta.x, iv.nokta.y, iv.nokta.z}; if (iv.normal.uzunlukKare() > 1e-8f) { const Vec3f n = iv.normal.birim(); vr.normal = {n.x, n.y, n.z}; vr.normalVar = true; vr.kaynak = SahneRefSecim::Kaynak::Yuz; } vr.govde = iv.govde; vr.alt = sahne.vurgu.alt; refSecim.tik(vr); // K5: SahneRefSecim pick olayı (makro/plugin/denetçi erişimi). cekirdek.olaylar().yayinla( RefSecimYapildi{refSecim.sahip(), vr.govde, vr.alt, (u32)vr.kaynak}, "RefSecim"); } } else if (sahne.vurgu.govde) { // Topolojik köşe/kenar/yüz: vurgulanan gövde+ordinal. Nokta konumu ışın-YÜZ // kesişiminden (mesh'li gövde); sketch gibi MESH'SİZ gövdede imlecVurus // başarısız olsa da govde/alt topolojik referans yeterli → tık yine kaydolur. vr.govde = sahne.vurgu.govde; vr.alt = sahne.vurgu.alt; Sahne::IsinVurus iv; if (imlecVurus(lx, ly, iv) && iv.govde) vr.nokta = {iv.nokta.x, iv.nokta.y, iv.nokta.z}; // opsiyonel dünya noktası refSecim.tik(vr); cekirdek.olaylar().yayinla( RefSecimYapildi{refSecim.sahip(), vr.govde, vr.alt, (u32)vr.kaynak}, "RefSecim"); } } else // Analiz sahneden-seçim (çekim yönü / hedef / kesit düzlem normali). if (analizSis.secimAktif()) { analizSis.tik(sahne.vurgu.govde, sahne.vurgu.alt, secimSis.vurguTur(), [&](Vec3f& nokta, Vec3f& normal) -> bool { Sahne::IsinVurus vr; if (imlecVurus(lx, ly, vr) && vr.govde) { nokta = vr.nokta; normal = vr.normal; return true; } return false; }); } else // CAM sahneden-seçim modu aktifse tıklama op geometrisine beslenir // (yüz/kenar konturu, delik noktası, düzlem Z, takım ekseni, stok gövde). if (camSis.secim().aktif()) { const bool ciftTik = ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left); camSis.secim().tik( sahne.vurgu, secimSis.vurguTur(), [&](Vec3f& nokta, Vec3f& normal) -> bool { Sahne::IsinVurus vr; if (imlecVurus(lx, ly, vr) && vr.govde) { nokta = vr.nokta; normal = vr.normal; return true; } return false; }, ciftTik); } else // Ölçüm penceresi açıksa tıklama seçim yerine ölçüme beslenir (CATIA akışı) if (olcum.acik && olcum.kip != OlcumKip::Ayarlar) { // Kumpas referans eksen seçim modu: tıklanan gövde/öğe → koordinat çerçevesi. if (olcum.eksenSecModu) { if (sahne.vurgu.govde) olcum.eksenAl(sahne.vurgu.govde, sahne.vurgu.alt, secimSis.vurguTur()); } else { // "Nokta Koy" modu: fare altındaki yüzeye izdüşen SERBEST noktayı al // (gövde-yerel/parametrik). Diğer filtreler topolojik seçime gider. Sahne::IsinVurus vr; if (olcum.aktifFiltre() == SecimFiltre::SerbestNokta) { if (imlecVurus(lx, ly, vr) && vr.govde) { olcum.serbestNoktaAl(vr.govde, vr.nokta); // K5: Ölçüm seçim ara-olayı (makro/plugin/denetçi erişimi). cekirdek.olaylar().yayinla(OlcumSecimYapildi{vr.govde, 0, true}, "Ölçüm"); } } else if (sahne.vurgu.govde) { olcum.secimAl(sahne.vurgu.govde, sahne.vurgu.alt, secimSis.vurguTur()); cekirdek.olaylar().yayinla( OlcumSecimYapildi{sahne.vurgu.govde, sahne.vurgu.alt, false}, "Ölçüm"); } } // eksenSecModu else } else { secimTiklaKaydet(cekirdek, sahne, secimSis, io.KeyCtrl); // Alt + genişlet (Plasticity): YÜZ kip → Ctrl+Shift+Alt=BENZER (aynı çap/boyut — // pattern), Shift+Alt=G1-TEĞET (fillet zinciri, eğri dahil), Ctrl+Alt=TÜM paralel, // Alt=koplanar. KENAR kip → Alt=edge loop (teğet zincir). if (io.KeyAlt && sahne.vurgu.govde) { const SecimOge v = sahne.vurgu; if (Dugum* d = sahne.bul(v.govde); d && d->brep) { std::vector kume; if (sahne.kip() == SecimKip::Yuz && secimSis.vurguTur() == 1) kume = (io.KeyCtrl && io.KeyShift) ? d->brep->benzerYuzler(v.alt) : io.KeyShift ? d->brep->tanjantYuzler(v.alt, dogrudanAyar().secimTolDeg) : io.KeyCtrl ? d->brep->paralelYuzler(v.alt, dogrudanAyar().secimTolDeg) : d->brep->koplanarYuzler(v.alt, dogrudanAyar().secimTolDeg); else if (sahne.kip() == SecimKip::Kenar && secimSis.vurguTur() == 2) kume = (io.KeyCtrl && io.KeyShift) ? d->brep->benzerKenarlar(v.alt) // aynı R/boy : d->brep->kenarZinciri(v.alt); // teğet zincir for (u32 k : kume) sahne.sec(SecimOge{v.govde, k}, true); } } } } } fare.sol = false; if (olcumSurukle && !ImGui::IsMouseDown(ImGuiMouseButton_Left) && !ImGui::IsMouseDown(ImGuiMouseButton_Right)) olcumSurukle = 0; } if (ImGui::IsMouseReleased(ImGuiMouseButton_Right)) { // Hareketsiz sağ tık → nesneye özel bağlam menüsü (vurgudaki gövde) // Orta tuş basılıyken (kamera pan/zoom) menü AÇILMASIN. if (fare.sag && !fare.kameraKullandi && !fare.orta && !ImGui::IsMouseDown(ImGuiMouseButton_Middle)) { const f32 kayma = std::abs(lx - fare.sagBasX) + std::abs(ly - fare.sagBasY); const auto sure = std::chrono::duration(simdi - fare.sagBasZaman).count(); if (kayma < 6.0f && sure < 0.5f) { const bool sketcherWb = cekirdek.workbenchler().aktif() == "sketcher"; SketchNesnesi* skR = sketchNesne(); // Sketcher'da araç aktifken sağ tık: değişken-tık aracı (spline/NURBS) // yeterli nokta biriktirmişse TAMAMLA (çift-tık ile aynı davranış); aksi // halde aracı iptal et (bağlam menüsü açma). if (sketcherWb && !sketchArac.empty()) { if ((sketchArac == "spline" || sketchArac == "nurbsEgri") && sketchNoktalar.size() >= 2 && skR) { sketchAracTamamla(skR->eskiz()); } sketchArac.clear(); sketchNoktalar.clear(); sketchDuzenFaz = 0; sketchSecili.clear(); } // Sketcher'da araç yokken sağ tık = DURUMA GÖRE bağlam menüsü (kısıt/geometri/boş). else if (sketcherWb && skR && aktifSketch != VARLIK_YOK) { eskiz::EskizVeri& evR = skR->eskiz(); u32 hitK = 0; // kısıt sembolü hit-test (overlay ekran-uzayı kutuları) for (const auto& hk : sketchDurum.kisitHitKutulari) if (io.MousePos.x >= hk.x0 && io.MousePos.x <= hk.x1 && io.MousePos.y >= hk.y0 && io.MousePos.y <= hk.y1) { hitK = hk.kimlik; break; } u32 hitG = 0; if (!hitK) { Dugum* dzR = sahne.bul(skR->duzlem()); f64 ur, vr; if (dzR && ekranUV(dzR, lx, ly, ur, vr)) hitG = sketchEnYakin(evR, ur, vr); // Hedef geometri seçili değilse seçime al → menü onun üzerinde çalışsın. if (hitG && std::find(sketchSecili.begin(), sketchSecili.end(), hitG) == sketchSecili.end()) { if (!io.KeyCtrl) sketchSecili.clear(); sketchSecili.push_back(hitG); } } sketchDurum.baglamKisit = hitK; sketchDurum.baglamGeo = hitG; sketchDurum.baglamAc = true; // sketchBaglamMenuCiz bu kare OpenPopup yapar } // ---- Doğrudan Modelleme: yüz/kenara sağ-tık → arama kutulu bağlam menüsü ---- // (radyal halkanın alternatifi; ayar "dogrudan.arama_menu" ile açık). Seçili // topolojiye göre komutlar; ilk öğe Sketch Oluştur. Sağ-tıklanan öğe önce seçilir. else if (cekirdek.workbenchler().aktif() == "dogrudan" && dogrudanAyar().aramaMenu) { // ---- DM birleşik modelleme menüsü: seçili topolojiye göre ÖNCELİKLİ komutlar + // TÜM Part Design/GSD komutları (arama kutusu filtreler). Tek workbench. ---- const u32 hg = sahne.vurgu.govde, ha = sahne.vurgu.alt; Dugum* vd = hg ? sahne.bul(hg) : nullptr; const bool datum = vd && (vd->tip == DugumTip::Duzlem || vd->tip == DugumTip::Eksen || vd->tip == DugumTip::EksenSistemi || vd->tip == DugumTip::Nokta || vd->tip == DugumTip::Cizgi); const bool duzlemDatum = vd && (vd->tip == DugumTip::Duzlem || (vd->tip == DugumTip::EksenSistemi && eksensis::duzlemMi(ha))); // GSD sonuc dugumleri: acik yuzey / wireframe — kati DEGIL. Niyet farkli // (yuzey → offset/uzat/kalinlastir; katı → fillet/shell). Ayri bağlam. const bool gsdYuzey = vd && vd->tip == DugumTip::Yuzey; const bool gsdTel = vd && vd->tip == DugumTip::Tel; // Sketch düğümü seçili → hem katı (Pad/Pocket) hem GSD yüzey (Extrude/Revolve/ // Fill) üretme niyeti. CATIA: sketch seç → yüzey/katı komutları aktif. const bool sketchDugum = (vd && vd->tip == DugumTip::Sketch) || sahne.seciliSketch() != VARLIK_YOK; // Sağ-tıklanan öğe komutların hedefi olsun (seçili değilse tekil seç). if (hg && !sahne.seciliMi({hg, ha})) { sahne.secimTemizle(); sahne.sec({hg, ha}, false); } bool duzYuz = false, ybVar = false; geometri::YuzBilgi yb; if (vd && vd->brep && vd->brep->gecerliMi() && ha && vd->brep->yuzBilgi(ha, yb)) { duzYuz = yb.duzlemMi; ybVar = true; } std::vector ml; std::vector eklendi; const auto anahtar = [](const std::string& g, const std::string& a) { return g + "\x1f" + a; }; // Registry komutunu ada göre menüye ekle: simge + etkinMi anlık durumu + ada-göre // çalıştırıcı (menü ömrü boyunca &cekirdek kararlı; kopya string yakalar). const auto ekle = [&](const char* grup, const char* ad, const char* ipucu = "") { const std::string k = anahtar(grup, ad); if (std::find(eklendi.begin(), eklendi.end(), k) != eklendi.end()) return; for (const MenuOge& o : cekirdek.menuler().hepsi()) if (o.grup == grup && o.ad == ad) { const bool etkin = !o.etkinMi || o.etkinMi(cekirdek); ml.push_back({o.ad, o.simge, ipucu, [&cekirdek, g = o.grup, a = o.ad] { for (const MenuOge& m : cekirdek.menuler().hepsi()) if (m.grup == g && m.ad == a) { cekirdek.komutCalistir(m, "dm_arama"); return; } }, etkin}); eklendi.push_back(k); return; } }; // Kalıp/döküm analizleri (Analiz WB'den DM'e): gövde/yüz bağlamında listenin // üst sırasında; hedefi seçili gövdedir (analizHedefSec komut içinde çözer). const auto analizEkle = [&ekle] { ekle("Analiz", "Çekme Açısı", "kalıp çıkışı"); ekle("Analiz", "Undercut", "ters açı"); ekle("Analiz", "Et Kalınlığı"); ekle("Analiz", "Radius"); ekle("Analiz", "Kurvatür"); ekle("Analiz", "Süreklilik"); ekle("Analiz", "Zebra"); ekle("Analiz", "Yansıma Hattı"); ekle("Analiz", "Kesit Düzlemi"); ekle("Analiz", "CAD Fark"); }; // ---- Seçim özeti: niyet SEÇİLİ topolojiden çıkar (DM ilkesi) ---- // vurgu (imleç altı) TEK öğedir; çoklu seçim varsa asıl niyet odur — // 3 yüz seçip sağ-tıklayınca menü tek-yüz bağlamı göstermemeli. // NOT: yukarıdaki "seçili değilse tekil seç" satırından SONRA okunur → // boşluğa/seçilmemişe sağ-tık zaten tekile düşürmüştür, tutarlı. u32 secGovdeSayi = 0, secAltSayi = 0; { std::vector govdeler; for (const SecimOge& s : sahne.secililer()) { if (s.alt) ++secAltSayi; if (std::find(govdeler.begin(), govdeler.end(), s.govde) == govdeler.end()) govdeler.push_back(s.govde); } secGovdeSayi = (u32)govdeler.size(); } const bool cokluGovde = secAltSayi == 0 && secGovdeSayi >= 2; const bool cokluAlt = secAltSayi >= 2; // Menü neyin üzerinde çalışacağını AÇIKÇA yazar (soluk, tıklanamaz başlık). // Yalnız çoklu seçimde: tekilde gürültü olurdu (bağlam zaten belli). if (cokluGovde || cokluAlt) { const char* tur = cokluGovde ? "gövde" : sahne.kip() == SecimKip::Kenar ? "kenar" : sahne.kip() == SecimKip::Nokta ? "nokta" : "yüz"; AramaMenuOge ozet; ozet.etiket = std::to_string(cokluGovde ? secGovdeSayi : secAltSayi) + " " + tur + " seçili"; ozet.simge = "▸"; ozet.etkin = false; // başlık: tıklanamaz ml.push_back(std::move(ozet)); } // ---- Bağlam kimliği: öğrenme kovası ("duruma göre" hatırlama) ---- // Aşağıdaki if-zincirinin dallarıyla BİREBİR aynı ayrım; delik yüzeyinde // öğrenilen komut boşluğa sağ-tıklayınca önerilmesin. Değerler kalıcılık // anahtarına girer → yeniden ADLANDIRMA geçmişi sıfırlar (yeni kova). dmBaglamAyarla( cokluGovde ? "coklu_govde" : (cokluAlt && sahne.kip() == SecimKip::Kenar) ? "coklu_kenar" : cokluAlt ? "coklu_yuz" : (ha && !datum && sahne.kip() == SecimKip::Kenar) ? "kenar" : (ha && !datum && duzYuz) ? "duz_yuz" : (ha && !datum && ybVar && yb.torusMu) ? "torus_yuz" : (ha && !datum && ybVar && yb.silindirMi) ? "silindir_yuz" : (ha && !datum) ? "egri_yuz" : gsdYuzey ? "gsd_yuzey" : gsdTel ? "gsd_tel" : sketchDugum ? "sketch" : duzlemDatum ? "duzlem_datum" : hg ? "govde" : "bosluk"); // ---- 🔁 Tekrarla: en son çalıştırdığın komut (bağlamdan BAĞIMSIZ) ---- // Akış: yüz seç → "aynı ölçüdeki yüzleri seç" → BAŞKA yüz seç → aynı komut. // Bu öğe olmadan her seferinde menüde aranıyordu. Bağlam kovasına BAKMAZ: // tekrar niyeti bağlam değişse de sürer (düz yüzde öğrenip silindirde // tekrarlamak isteyebilirsin). HEP EN ÜSTTE → kas hafızası + tek dokunuş // (dokunmatikte klavye yok). ekle() yinelemeyi engeller: aşağıdaki bağlam // bloğu aynı komutu ikinci kez eklemez. { std::string sonGrup, sonAd; if (komutSonCalisan(sonGrup, sonAd)) ekle(sonGrup.c_str(), sonAd.c_str(), "🔁 son komut — tekrarla"); } // ---- Bağlama göre ÖNCELİKLİ komutlar (listenin başında) ---- // Çoklu seçim ÖNCE: vurgu tek öğeyi gösterse de niyet seçimdedir. if (cokluGovde) { // İki+ gövde = Boolean niyeti (komutların kendi etkinIkiGovde koruması var // → 3+ gövdede soluk görünür, yanlış çalışmaz). ekle("Boolean", "Birleştir", "seçili gövdeleri birleştir"); ekle("Boolean", "Çıkar (A−B)", "ilkinden ötekini oy"); ekle("Boolean", "Kesişim", "ortak hacim"); ekle("Yüzey", "Kesişim Eğrisi", "kesişim hattı (tel, katı değil)"); ekle("Yüzey", "Köprüle", "iki kenar arasına yüzey"); ekle("Feature", "Ayna (Mirror)"); ekle("Ölçüm", "Ölçüm Penceresi (M)", "gövdeler arası"); analizEkle(); } else if (cokluAlt && sahne.kip() == SecimKip::Kenar) { ekle("İşlem", "Kenar Yuvarlat (3B)", "seçili kenarlar"); ekle("İşlem", "Pah (3B)", "seçili kenarlar"); ekle("Ölçüm", "Ölçüm Penceresi (M)"); } else if (cokluAlt) { // çoklu yüz ekle("Değiştir", "Yüz İt/Çek", "seçili yüzler"); ekle("İşlem", "Kenar Yuvarlat (3B)"); ekle("İşlem", "Pah (3B)"); ekle("Değiştir", "Yüz Kaldır", "seçili yüzleri sil"); ekle("Ölçüm", "Ölçüm Penceresi (M)", "yüzler arası"); analizEkle(); } else if (ha && !datum && sahne.kip() == SecimKip::Kenar) { ekle("İşlem", "Kenar Yuvarlat (3B)"); ekle("İşlem", "Pah (3B)"); // Tek kenardan niyet: teğet zincir (tüm loop) / aynı R'deki filletler. ekle("Seçim", "◠ Teğet kenar zincirini seç (loop)"); ekle("Seçim", "◯ Aynı ölçüdeki kenarları seç (bu R'deki filletler)"); ekle("Ölçüm", "Ölçüm Penceresi (M)"); ekle("IJK", "Bu Yönü IJK Hedefine Gönder", "kenar doğrultusu → Kafa penceresi"); } else if (ha && !datum) { // Yüz if (duzYuz) { ekle("Sketch", "Sketch Oluştur", "yüze çiz"); ekle("Değiştir", "Sketch Pad", "profili çek"); ekle("Değiştir", "Sketch Cep", "profili oy"); // Düzlem yüz → kardeşleri tek hamlede seç (koplanar/paralel/eş ölçü). ekle("Seçim", "▱ Koplanar yüzleri seç"); ekle("Seçim", "∥ Paralel yüzleri seç"); ekle("Seçim", "⊘ Aynı ölçüdeki yüzleri seç (bu çaptaki delikler)"); } else { // eğri yüz // ---- Geometrik akıl: yüzey TİPİ niyeti söyler (BREP'ten, mesh'ten değil) ---- // Torus = fillet izi → yüzü kaldırmak filleti SİLER. Silindir = delik/boss // → yüzü kaldırmak deliği kapatır; "aynı ölçüdeki yüzler" o çaptaki TÜM // delikleri seçer (akıllı seçim motoru hazır — bkz. akilli_secim). if (ybVar && yb.torusMu) { ekle("Değiştir", "Yüz Kaldır", "filleti sil"); ekle("Seçim", "◯ Aynı ölçüdeki kenarları seç (bu R'deki filletler)"); } else if (ybVar && yb.silindirMi) { ekle("Değiştir", "Yüz Kaldır", "deliği kapat"); ekle("Seçim", "⊘ Aynı ölçüdeki yüzleri seç (bu çaptaki delikler)"); } ekle("Seçim", "◠ Teğet kenar zincirini seç (loop)"); ekle("Sketch", "Sketch Oluştur", "teğet düzlem"); ekle("Değiştir", "Sketch Kabart", "yüzeye sar"); ekle("Değiştir", "Sketch Oy", "yüzeye oy"); } ekle("Değiştir", "Yüz İt/Çek"); if (duzYuz) ekle("Değiştir", "Yüz Taşı", "3-eksen sürükle"); if (duzYuz) ekle("Değiştir", "Blok Kaydır", "çıkıntıyı taban boyunca kaydır"); if (duzYuz) ekle("IJK", "Bu Yönü IJK Hedefine Gönder", "yüz normali → Kafa penceresi"); ekle("Yüzey", "Yüzeye Çıkar", "GSD yüzeye kopyala (hibrit)"); ekle("İşlem", "Kenar Yuvarlat (3B)"); ekle("İşlem", "Pah (3B)"); ekle("Değiştir", "Yüz Kaldır"); ekle("Ölçüm", "Ölçüm Penceresi (M)"); analizEkle(); } else if (sketchDugum) { // Sketch seçili → katı + GSD yüzey üretme ekle("Değiştir", "Sketch Pad", "profili çek (katı)"); ekle("Değiştir", "Sketch Cep", "profili oy"); ekle("Yüzey", "Yüzey Extrude", "profili yüzeye çek"); ekle("Yüzey", "Yüzey Revolve", "profili döndür"); ekle("Yüzey", "Yüzey Fill", "kapalı profili doldur"); ekle("Ölçüm", "Ölçüm Penceresi (M)"); } else if (gsdYuzey) { // GSD açık yüzey → yüzey-üzeri op'lar (niyet: düzenle) ekle("Yüzey", "Yüzey Offset", "paralel yüzey"); ekle("Yüzey", "Yüzey Uzat", "sınırları uzat"); ekle("Yüzey", "Yüzey Kalınlaştır", "katıya çevir"); ekle("Yüzey", "Yüzey Sınırı", "kenar teli çıkar"); ekle("Yüzey", "Afin (Non-uniform)", "eksen-bağımsız ölçek"); ekle("Yüzey", "Yüzey Şişir", "NURBS kubbe/çukur"); ekle("Yüzey", "Yüzey Yumuşat", "pürüz/kubbe relax"); ekle("Yüzey", "Onar (Heal)", "topolojiyi düzelt"); ekle("Ölçüm", "Ölçüm Penceresi (M)"); analizEkle(); } else if (gsdTel) { // GSD wireframe → yüzey türet / ölç / birleştir ekle("Yüzey", "Yüzey Extrude", "teli yüzeye çek"); ekle("Yüzey", "Yüzey Revolve", "teli döndür"); ekle("Yüzey", "Eğri Birleştir", "kenarları tek eğri yap"); ekle("Ölçüm", "Ölçüm Penceresi (M)"); } else if (duzlemDatum) { // referans/origin düzlem → sketch + GSD yüzey ekle("Sketch", "Sketch Oluştur", "çiz"); ekle("Tel-Kafes", "Çizgi", "düzlemde çiz"); ekle("Tel-Kafes", "Çember"); ekle("Tel-Kafes", "Spline"); } else if (hg) { // katı gövde ekle("Feature", "Fillet (Kenar Yuvarlat)"); ekle("Feature", "Chamfer (Pah)"); ekle("Feature", "Shell (İçi Boşalt)"); ekle("Feature", "Draft (Yüz Açısı)"); ekle("Feature", "Ayna (Mirror)"); ekle("Feature", "Dik Dizi"); ekle("Feature", "Dairesel Dizi"); analizEkle(); } else { // boşluk → içe aktar / oluştur ekle("Dosya", "STEP İçe Aktar...", "model getir"); ekle("Dosya", "Aç (.fikocad)..."); ekle("Ekle", "Body", "yeni gövde (In Work Object)"); ekle("Geometri", "Kutu"); ekle("Geometri", "Silindir"); ekle("Geometri", "Küre"); ekle("Geometri", "Koni"); // GSD wireframe paleti (bağımsız — sketch gerekmez): gerçek OCCT B-Rep. ekle("Tel-Kafes", "Nokta"); ekle("Tel-Kafes", "Çizgi"); ekle("Tel-Kafes", "Çember"); ekle("Tel-Kafes", "Spline"); ekle("Tel-Kafes", "Polyline"); ekle("Tel-Kafes", "Helis"); ekle("Tel-Kafes", "Konik"); ekle("Tel-Kafes", "Köşe"); ekle("Dosya", "STEP Dışa Aktar..."); ekle("Dosya", "Kaydet (.fikocad)..."); analizEkle(); } // ---- Klavyesiz erişim: Geri Al / Sil / Temizle / Çerçevele ---- // DOKUNMATİKTE KLAVYE YOK → Ctrl+Z, Delete, Esc ulaşılamaz. Bu komutlar // registry'de VARDI ama hiçbir bağlamda öncelikli değildi; yalnız "tüm // komutlar" bloğunda, ARANARAK bulunuyordu → parmakla pratikte erişilmez. // Kendi etkinMi korumaları var (yapılacak iş yoksa soluk görünür). if (hg) { ekle("Görünüm", "Seçimi Çerçevele", "seçiliye yakınlaş"); ekle("Düzenle", "Seçileni Sil"); ekle("Düzenle", "Seçimi Temizle"); } else { ekle("Görünüm", "Tümünü Sığdır", "her şeyi ekrana getir"); } ekle("Düzenle", "Geri Al", "son işlemi geri al"); ekle("Düzenle", "Yinele"); // Seçim kipi: AKTİF OLAN hariç öteki üçü (klavyesiz 1-4 karşılığı). // Aktifi listelemek gürültü — zaten o kiptesin. if (sahne.kip() != SecimKip::Govde) ekle("Seçim", "◼ Gövde kipi (1)"); if (sahne.kip() != SecimKip::Yuz) ekle("Seçim", "▢ Yüz kipi (2)"); if (sahne.kip() != SecimKip::Kenar) ekle("Seçim", "╱ Kenar kipi (3)"); if (sahne.kip() != SecimKip::Nokta) ekle("Seçim", "• Nokta kipi (4)"); // ---- Gizle / Göster (seçili gövde/datum) — her bağlamda kullanılabilir ---- if (hg && vd) { const bool gor = vd->gorunur; AramaMenuOge go; go.etiket = gor ? "Gizle" : "Göster"; go.simge = gor ? "🚫" : "👁"; go.calistir = [&bglm, hg, gor] { bglm.gorunurDegistir(hg, !gor); }; ml.push_back(std::move(go)); } // ---- Öğrenilenler: bu DURUMDA son + sık kullandıkların ---- // Bağlam önceliği HEP üstte kalır (niyet > alışkanlık); bu blok onun // ALTINDA, tüm komutlardan ÖNCE. Bağlamda zaten eklenenler atlanır // (ekle() eklendi[] ile yinelemeyi engeller). for (const OgrenilenKomut& o : komutOgrenilen(dmBaglam(), 3, 3)) ekle(o.grup.c_str(), o.ad.c_str(), o.son ? "son kullandığın" : "sık kullandığın"); // ---- TÜM komutlar — arama kutusu daraltır ---- // DM tek ortam: Part/GSD + Analiz (undercut/draft/kalınlık…) + Döküm + global // (Dosya "STEP İçe Aktar", Ölçüm, Düzenle…) hepsi burada. Sketcher hariç: // 52 çizim aracı aktif sketch ister, sketch'teyken zaten kendi menüsü açılır. for (const MenuOge& o : cekirdek.menuler().hepsi()) { if (o.workbench == "sketcher") continue; const std::string k = anahtar(o.grup, o.ad); if (std::find(eklendi.begin(), eklendi.end(), k) != eklendi.end()) continue; const bool etkin = !o.etkinMi || o.etkinMi(cekirdek); ml.push_back({o.ad, o.simge, "", [&cekirdek, g = o.grup, a = o.ad] { for (const MenuOge& m : cekirdek.menuler().hepsi()) if (m.grup == g && m.ad == a) { cekirdek.komutCalistir(m, "dm_arama"); return; } }, etkin}); eklendi.push_back(k); } // ---- PMI/GD&T önerileri (niyet SEÇİLİ TOPOLOJİDEN — pmi::niyetOnerileri) ---- // Vurgulanan yüz düz/silindirik mi → uygun karakteristikler + "Datum yap". // Datum harfi otomatik; hedef KYA kalıcı kimlikle bağlanır (ordinal kayması // bağışıklığı). Öğe defter + ürün ağacı düğümü olarak doğar, çapa hemen çözülür. if (vd && vd->brep && vd->brep->gecerliMi() && sahne.vurgu.alt != 0 && secimSis.vurguTur() == 1) { // yüz vurgusu geometri::YuzBilgi yb; if (vd->brep->yuzBilgi(sahne.vurgu.alt, yb)) { PmiSistemi& pmiSisRef = belge.pmiSis; int datumSayi = 0; for (const pmi::PmiOge& po : pmiSisRef.defter().hepsi()) if (po.tip == pmi::PmiTip::Datum) ++datumSayi; const u32 pmiGovde = hg; const u32 pmiAlt = sahne.vurgu.alt; const u64 pmiKid = vd->brep->yuzKimlik(pmiAlt); const auto pmiOlustur = [&pmiSis = pmiSisRef, &sahne, &cekirdek = cekirdek, pmiGovde, pmiAlt, pmiKid](pmi::PmiOge o, std::string ad) { o.hedefler.push_back({pmiGovde, pmiAlt, 1, pmiKid}); o.ofsetYerel = {70, 50, 0}; // çerçeve çapanın sağ üstünde doğar const u32 pk = pmiSis.defter().ekle(o); if (pmi::PmiOge* oo = pmiSis.defter().bul(pk)) pmiSis.capaCoz(*oo); sahne.pmiEkle(&pmiSis, pk, std::move(ad)); }; bool datumOner = false; for (const pmi::NiyetOneri& n : pmi::niyetOnerileri(yb.duzlemMi, yb.silindirMi, datumSayi, datumOner)) { pmi::PmiOge o; o.tip = pmi::PmiTip::Gdt; o.gdt[0].karakter = n.karakter; o.gdt[0].capsal = n.capsal; if (n.datumIster) o.gdt[0].datum[0] = 'A'; // ilk datum referansı std::string ad = std::string(pmi::karakterGlif(n.karakter)) + " " + pmi::karakterAdi(n.karakter); ml.push_back({std::string(pmi::karakterAdi(n.karakter)) + " toleransı", pmi::karakterGlif(n.karakter), "GD&T çerçevesi ekle", [pmiOlustur, o, ad] { pmiOlustur(o, ad); }, true}); } if (datumOner) { pmi::PmiOge o; o.tip = pmi::PmiTip::Datum; o.datumHarf = pmiSisRef.defter().sonrakiDatumHarfi(); const std::string ad = std::string("Datum ") + o.datumHarf; ml.push_back({"Datum yap (" + std::string(1, o.datumHarf) + ")", "▲", "datum özelliği işareti", [pmiOlustur, o, ad] { pmiOlustur(o, ad); }, true}); } } } // Boş alan: not/bayrak (hedefsiz, kamera odağına çapalanmaz — serbest). if (!vd) { ml.push_back({"Not ekle", "🗒", "3B sahneye serbest not", [&pmiSis = belge.pmiSis, &sahne, &kamera] { pmi::PmiOge o; o.tip = pmi::PmiTip::Not; o.metin = "Not"; o.gecerli = true; o.liderVar = false; o.capa = kamera.odak(); // bakılan yerde doğar (0,0,0 değil) const u32 pk = pmiSis.defter().ekle(o); sahne.pmiEkle(&pmiSis, pk, "Not"); }, true}); } // ---- "Ağırlık / Kütük" (Hızlı 3D widget) ---- // Bu widget komut kaydında DEĞİL; tek erişimi fare SOL uzun-basışıydı // (~4421). Dokunmatikte uzun bas bağlam menüsünü açtığı için parmakla // ulaşılamaz kalıyordu → menüye köprü. Açılış koşulu fare yoluyla BİREBİR: // katı/mesh VEYA Eksen/EksenSistemi (mesh'siz datum → kütük NaN/çökme). if (vd) { const bool ozelTip = vd->tip == DugumTip::Eksen || vd->tip == DugumTip::EksenSistemi; if (vd->indeksSayi > 0 || ozelTip) { const ImVec2 acilis = ImGui::GetMousePos(); ml.push_back({"Ağırlık / Kütük", "⚖", "hacim, kütle, kütük ölçüsü", [&hizli3dKimlik, &hizli3dAcik, &hizliDurum, hg, acilis] { hizli3dKimlik = hg; hizli3dAcik = true; hizliDurum.hizli3dAcilisX = acilis.x; hizliDurum.hizli3dAcilisY = acilis.y; }, true}); } } aramaMenuAc(ort.dmAramaMenu, ImGui::GetMousePos(), std::move(ml)); // Grafik özellik bandı: filtre boşken arama kutusunun ALTINA kompakt renk/KYA // satırı (radyal toolbar ile TEK kaynak — grafikOzellikSatiriCiz). Yükseklik // açılışta sabitlenir (seçim menü boyunca değişmez); bglm pencere ömürlü. if (dogrudanAyar().aramaMenuOzellik) { ort.dmAramaMenu.ustPanelYuk = grafikOzellikYuk(sahne); if (ort.dmAramaMenu.ustPanelYuk > 0.0f) { ort.dmAramaMenu.ustPanel = [&bg = bglm, &m = ort.dmAramaMenu] { grafikOzellikSatiriCiz(bg, true); // "ada göre seç" kip/seçim değiştirdiyse menü içeriği bayat → kapat. if (grafikOzellikSecimSicradi()) m.kapatIstegi = true; }; ort.dmAramaMenu.kapanis = [&bg = bglm] { grafikOzellikOturumTamamla(bg); }; } } } // Jest yolu bu tık için menü açmadıysa: sağ-tık bağlam menüsünü BURADAN aç // (işaret/radyal menü — legacy dikey popup yerine). sahne.vurgu güvenilir // çözülü olduğundan bu, jest hover zamanlamasından bağımsız GARANTİ tetikler. // Menü ZATEN açıksa da yeniden açar (başka yere sağ-tık → oraya taşı). else if (!baglamMenuAcildi) { girdi::FareJesti sj; sj.tur = girdi::JestTur::Tik; sj.dugme = girdi::Dugme::Sag; sj.hedef.govdeId = sahne.vurgu.govde; // DATUM (referans düzlem / Origin eksen-sistemi / eksen) → Govde menüsü (Eskiz // Aç). Kip ne olursa datum'a kenar/pah menüsü açılmaz (kip=filtre, tür değil). Dugum* vd2 = sahne.bul(sahne.vurgu.govde); const bool datumDugum2 = vd2 && (vd2->tip == DugumTip::Duzlem || vd2->tip == DugumTip::Eksen || vd2->tip == DugumTip::EksenSistemi || vd2->tip == DugumTip::Nokta || vd2->tip == DugumTip::Cizgi); if (!sahne.vurgu.govde) { sj.hedef.tur = girdi::HedefTur::Hic; } else if (datumDugum2) { sj.hedef.tur = girdi::HedefTur::Govde; } else { switch (sahne.kip()) { case SecimKip::Yuz: sj.hedef.tur = girdi::HedefTur::Yuz; break; case SecimKip::Kenar: sj.hedef.tur = girdi::HedefTur::Kenar; break; case SecimKip::Nokta: sj.hedef.tur = girdi::HedefTur::Nokta; break; default: sj.hedef.tur = girdi::HedefTur::Govde; break; } } // Dalga 3: motor kapsıyorsa halka motordan; değilse BaglamPolitika (regresyon yok). girdi::PolitikaKarar k; const auto& mk = belge.baglamMotoru.sonKarar(); if (!datumDugum2 && baglamMotorKapsar(mk.hedef.kategori) && !mk.ogeler.empty()) { sonMotorOneri_ = mk.ogeler; baglamPolitikaYap(sonMotorOneri_, k); } else { baglamPol.tekWorkbench = dogrudanAyar().tekWorkbench; { const auto& sl = sahne.secililer(); baglamPol.ikiYuzFarkliGovdeSecili = sahne.kip() == SecimKip::Yuz && sl.size() == 2 && sl[0].govde != sl[1].govde && sl[0].alt != 0 && sl[1].alt != 0; } k = baglamPol.karar(sj, cekirdek.workbenchler().aktif()); // datum → Eskiz Aç } if (k.gecerli()) { // Eksen DOĞRUSU (Eksen düğümü veya EksenSistemi eksen-ordinali 1/2/3) düzlem // DEĞİL → "Eskiz Aç" öğesini kaldır (eksende sketch olmaz). Yalnız referans // düzlem / eksen-sistemi düzlem-ordinali (4/5/6) / düz yüzde kalır. if (datumDugum2) { bool duzlemUygun = false; if (vd2->tip == DugumTip::Duzlem) duzlemUygun = true; else if (vd2->tip == DugumTip::EksenSistemi) duzlemUygun = eksensis::duzlemMi(sahne.vurgu.alt); if (!duzlemUygun) k.ogeler.erase( std::remove_if(k.ogeler.begin(), k.ogeler.end(), [](const girdi::MenuOgeSecim& o) { return o.komutKimlik == "sketch_yuz"; }), k.ogeler.end()); } // Açık menü varsa yeni konumda yeniden aç: eski halka durumunu sıfırla. if (sonPolitika.gecerli()) ui::aracHalkasiSifirla("baglam"); sonPolitika = std::move(k); sonPolitikaKonum = ImGui::GetMousePos(); sonPolitikaHedef = sahne.vurgu.govde; sonPolitikaHedefAlt = sahne.vurgu.alt; // datum düzlem 4/5/6 / yüz (Eskiz Aç) // aracHalkasi (radyal, nested) çizim bloğunda açılır. } } } } fare.sag = false; } baglamMenuAcildi = false; // her karede sıfırla (jest yeniden set eder) if (ImGui::IsMouseReleased(ImGuiMouseButton_Middle)) { // Hareketsiz orta tık = merkezle + döndürme pivotunu oraya taşı (imleç yüzeyi). if (fare.orta && !fare.kameraKullandi) { const f32 kayma = std::abs(lx - fare.ortaBasX) + std::abs(ly - fare.ortaBasY); const auto sure = std::chrono::duration(simdi - fare.ortaBasZaman).count(); if (kayma < 5.0f && sure < 0.35f) { Vec3f h; if (pickAyar().ortaMerkezAkilli) { // Yüzey ∨ gövde bulunursa oraya merkezle; gerçek boşlukta pivotu KORU // (eskiden imlecDunya "havada bir noktaya" ışınlayıp orbit'i parçaya daldırıyordu). if (imlecMerkez(lx, ly, h)) kamera.merkezle(h); } else { kamera.merkezle(imlecYuzey(lx, ly, h) ? h : imlecDunya(lx, ly)); } } } fare.orta = false; } // Tüm tuşlar bırakılınca kamera-kullanım bayrağını sıfırla (yeni sürükleme temiz). if (!fare.sol && !fare.orta && !fare.sag) fare.kameraKullandi = false; } else { vpHover = false; } if (!aracKipi) ImGui::End(); // Begin atlandıysa End de atlanır (yukarıdaki koşulun eşi) ImGui::PopStyleVar(); // ---- Ek belge pencereleri (MDI Dalga 2): Image + jest + tık seçimi ---- if (!aracKipi) belgePencereleriCiz(belgeYon); kafaPencereCiz(vulkan, bglm); // kafa panelleri (üstte 3D + altta sekmeler) #ifdef FIKO_CEF yardimPenceresiCiz(vulkan, arayuz); // gömülü CEF yardım paneli (OSR → ImGui::Image) #endif // ---- Taşınabilir workbench araç çubukları (default: 3D Görünüm içinde) ---- // Aktif workbench + global komutların grup'ları başına yüzen toolbar. Default // konum 3D Görünüm penceresi içinde (üst kenarda yatay dizili); sürüklenip // bırakılınca en yakın kenara dayanır/yapışır (snap). Yeni araç = MenuOge'ye // simge + workbench vermek (merkezi sistem). 3D Görünüm'den SONRA çizilir → üstte kalır. // GEÇİCİ: kullanıcı isteğiyle taşınabilir araç çubukları şimdilik gizli // (oturum-içi bayrak; kodu silme — false→true ile kolayca geri açılır). static bool g_wbToolbarGoster = false; if (g_wbToolbarGoster) { const std::string& aktifWb = cekirdek.workbenchler().aktif(); const auto gorunur = [&](const MenuOge& o) { return !o.simge.empty() && (o.workbench.empty() || o.workbench == aktifWb); }; std::vector gruplar; for (const MenuOge& o : cekirdek.menuler().hepsi()) if (gorunur(o) && std::find(gruplar.begin(), gruplar.end(), o.grup) == gruplar.end()) gruplar.push_back(o.grup); // 3D Görünüm ekran dikdörtgeni (bu kare): sol-üst + boyut. const f32 vx0 = vpRectMinX, vy0 = vpRectMinY; const f32 vx1 = vx0 + (f32)istenenVpG, vy1 = vy0 + (f32)istenenVpY; const f32 bosluk = 8.0f, snapEsik = 22.0f; // 3D Görünüm konumlanana kadar (ilk kareler, dockspace kurulumu) toolbar çizme: // aksi halde FirstUseEver (0,0)'a kilitlenir ve ana pencerede kalır. const bool vpGecerli = istenenVpG >= 120 && istenenVpY >= 120 && vx0 >= 1.0f; // CATIA tarzı kompakt görünüm: küçük sıkışık ikon butonları, ince kenar boşluğu. ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(4, 3)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(2, 2)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(3, 3)); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); // düz (flat) — hover'da belirir const ImVec2 dugmeBoyut(26, 24); int idx = 0; for (const std::string& g : gruplar) { if (!vpGecerli) break; // 3D Görünüm hazır değil → bu kare toolbar yok const std::string pencereKimlik = g + "###wbtb_" + g; ImGui::SetNextWindowPos({vx0 + bosluk + (f32)idx * 190.0f, vy0 + bosluk}, ImGuiCond_FirstUseEver); ustteTutSinif(true); // ana pencereye owner → 3D Görünüm arkasına düşmez if (ImGui::Begin(pencereKimlik.c_str(), nullptr, ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoSavedSettings)) { // Sürükleme tutamacı (CATIA grip) — görsel; sürükleme başlık çubuğundan. ImGui::TextDisabled("⁝⁝"); ImGui::SameLine(0.0f, 4.0f); int bid = 0; for (const MenuOge& o : cekirdek.menuler().hepsi()) { if (o.grup != g || !gorunur(o)) continue; if (o.etkinMi && !o.etkinMi(cekirdek)) continue; // etkin değil → gizle if (bid) ImGui::SameLine(0.0f, 2.0f); ImGui::PushID(bid++); if (ImGui::Button(o.simge.c_str(), dugmeBoyut)) cekirdek.komutCalistir(o, "toolbar"); if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", o.ad.c_str()); ImGui::PopID(); } // Kenara dayama/snap: sürüklenmiyorken 3D Görünüm kenarına yakınsa yapış. const bool suruklu = ImGui::IsWindowFocused() && ImGui::IsMouseDragging(ImGuiMouseButton_Left); if (!suruklu) { const ImVec2 pos = ImGui::GetWindowPos(); const ImVec2 sz = ImGui::GetWindowSize(); f32 nx = pos.x, ny = pos.y; if (std::abs(pos.x - (vx0 + bosluk)) < snapEsik) nx = vx0 + bosluk; else if (std::abs((pos.x + sz.x) - (vx1 - bosluk)) < snapEsik) nx = vx1 - bosluk - sz.x; if (std::abs(pos.y - (vy0 + bosluk)) < snapEsik) ny = vy0 + bosluk; else if (std::abs((pos.y + sz.y) - (vy1 - bosluk)) < snapEsik) ny = vy1 - bosluk - sz.y; // 3D Görünüm dışına tamamen kaçmasın (başlık hep erişilebilir kalsın). nx = std::clamp(nx, vx0 - sz.x + 46.0f, vx1 - 46.0f); ny = std::clamp(ny, vy0, vy1 - 24.0f); if (nx != pos.x || ny != pos.y) ImGui::SetWindowPos({nx, ny}); } } ImGui::End(); idx++; } ImGui::PopStyleColor(); ImGui::PopStyleVar(3); } // ---- Ürün ağacı (urun_agaci_paneli.*'ye taşındı) ---- // ---- Polimorfik panel çizimi (FikoPencereYonetici) — eski 6 tek tek çağrı yerine. // Kayıt sırası (kur()): Ürün Ağacı → Işık → Özellikler → Olay → Makro → Eklenti. // Paneller görünürlüğü kendi içinde denetler → cizTumu her kareyi sürer (BİREBİR). g_sonAdim = "kareCiz: erken paneller"; // faz izleyici { FIKO_ADIM("paneller cizTumu"); panelYon_.cizTumu(bglm); } // Lisans Üretici (yalnız üretici makinede açılabilir; müşteride bayrak hiç true olmaz). #ifdef FIKO_URETICI_UI if (g_lisansUreticiAcik) lisansUreticiPenceresiCiz(bglm, g_lisansUreticiAcik); // Yönetim Konsolu — bağlı cihazlara uzaktan lisans/komut (üretici only). if (g_lisansYonetimAcik) lisansYonetimCiz(bglm, g_lisansYonetimAcik); // Cihaz Merkezi — sade kart ızgarası ve cihaz sayfası (üretici only). if (g_cihazMerkeziAcik) cihazMerkeziCiz(bglm, g_cihazMerkeziAcik); #endif ui::galeriCiz(); // ---- Kontrol Merkezi (Ctrl+Space) — eski üst menünün yerine tek merkez ---- // Bağlam her kare ucuz doldurulur: küçük tanımlayıcı vektörleri (bool* / std::function). // Panel toggle'ları, seçim filtreleri, trap, workbench ve compass hepsi buraya taşındı. if (ort.komutPalet.acikMi()) { KontrolMerkeziBaglam km; km.palet = &ort.komutPalet; km.cekirdek = &cekirdek; km.metrik = &ort.metrikToplayici.metrik(); km.fpsMs = kareSuresiMs; km.hizliEylemler = { {"✕", "Sil", false, true, [&] { seciliSil(); }, "Seçili gövdeleri sil"}, {"⛶", "Sığdır", false, true, [&] { tumunuSigdir(); }, "Tümünü ekrana sığdır"}, {"▭", "Alt Şerit", g_serit.gorunurMu(), true, [&] { if (g_serit.gorunurMu()) g_serit.gizle(); else g_serit.goster(); }, "Alt şerit çubuğunu aç/kapat"}, {"🔍", "Ağacı Sığdır", false, agacIcinde, [&] { agacSigdir = true; }, "Sahne-içi ağaç zoom/kaydırmasını sıfırla (Shift+F3 = ağaç odağı)"}, }; km.paneller = { {"🗂", "Ürün Ağacı", &panelAgac, nullptr}, {"↳", "Ağaç 3B İçinde", &agacIcinde, "Ürün ağacını 3B görünüm içinde göster"}, {"💡", "Işıklar", &panelIsik, nullptr}, {"📋", "Özellikler", &panelOzellik, nullptr}, {"📜", "Olay Denetçi", &panelOlay, nullptr}, {"⚙", "İşlemler", &panelIsler, nullptr}, {"✏", "Sketcher", &panelSketch, nullptr}, {"📏", "Ölçüm", &olcum.acik, "Ölçüm penceresi (M)"}, {"🐢", "Makro Konsolu", &panelMakro, nullptr}, {"🧩", "Eklenti Yöneticisi", &panelEklenti, nullptr}, {"✨", "FikoUi Galerisi", &ui::galeriAcik(), nullptr}, }; // Açık belge pencereleri (MDI) — eski "Pencereler ▸ Belgeler" bölümü. for (auto& bk : belgeYon.hepsi()) km.paneller.push_back({"📄", bk->ad, &bk->acik, "Belge penceresi"}); km.secimFiltreleri = { {"◧", "Yüz", &secimSis.fYuz, "1"}, {"▤", "Kenar", &secimSis.fKenar, "2"}, {"•", "Nokta", &secimSis.fNokta, "3"}, {"◼", "Gövde", &secimSis.fGovde, "4"}, }; { struct { const char* em; TrapTip t; } tr[] = { {"▭", TrapTip::Dikdortgen}, {"▢", TrapTip::KesenDikdortgen}, {"⬠", TrapTip::Poligon}, {"✎", TrapTip::FircaIzi}, {"▣", TrapTip::DisRadikdortgen}, {"◪", TrapTip::DisKesen}}; for (auto& d : tr) km.trapAraclari.push_back( {d.em, trapAdi(d.t), alan.tip == d.t, true, [&alan, tip = d.t] { alan.tip = tip; }, "T ile döngü"}); } // DM TEK WORKBENCH (Fikri kararı): seçici GİZLENİR — workbench kavramı kullanıcıya // sunulmaz (komutlar arama menüsü/sağ-tık/paletten; sketch-pafta MOD geçişleri kod // içinden aktifAyarla ile sürer, etkilenmez). Ayar kapalıyken eski seçici birebir. if (!dogrudanAyar().tekWorkbench) for (const WorkbenchOge& wb : cekirdek.workbenchler().hepsi()) km.workbenchler.push_back( {wb.simge.empty() ? "🧭" : wb.simge.c_str(), wb.ad, wb.kimlik == cekirdek.workbenchler().aktif(), true, [&cekirdek, kimlik = wb.kimlik] { cekirdek.workbenchler().aktifAyarla(cekirdek, kimlik); }, nullptr}); { const bool secimVar = !sahne.seciliGovdeler().empty(); km.compassEylemleri = { {"🧲", "Seçilene Yapıştır", pusula.bagli, secimVar, [&] { const auto sg = sahne.seciliGovdeler(); if (!sg.empty()) { pusula.bagli = true; pusula.govde = sg.back(); pusula.tutamac = -1; } }, "Compass'ı seçili gövdeye yapıştır (G)"}, {"↩", "Bırak (köşeye)", false, pusula.bagli, [&] { pusula.bagli = false; pusula.tutamac = -1; }, "Compass'ı köşeye bırak (Esc)"}, }; } kontrolMerkeziCiz(km); } // ---- Dinamik araç çubuğu (B): görünür toolbar'ları çiz + sürükle/snap ---- if (!aracKipi) g_araclar.ciz(); // ---- Dinamik durum-farkında alt şerit (registry→filtre→çiz, kopyasız) ---- if (!aracKipi) g_serit.ciz(cekirdek); // ---- Hover bilgi karti (G) -> hover_karti.cpp ---- if (!aracKipi) hoverBilgiKartCiz(sahne, hoverProbu, hoverBilgiAcik, vpHover, kare); // ---- Perf HUD -> perf_hud.cpp ---- perfHudCiz(); // ---- Baglam ipucu HUD -> baglam_hud.cpp ---- if (!aracKipi) baglamIpucuHudCiz(belge, ort.hoverHudAcik, vpHover, sonPolitika); // ---- Baglam politikasi radyal menusu -> baglam_menu.cpp ---- baglamPolitikaMenuCiz(bglm, io, sonPolitika, sonPolitikaHedef, sonPolitikaKonum, hizliDurum, panelSketch); // ---- Doğrudan Modelleme arama kutulu bağlam menüsü -> arama_menu.cpp ---- aramaMenuCiz(ort.dmAramaMenu, io); // ---- Montaj Kısıtları: Mesafe/Açı sayısal değer diyaloğu ---- if (bglm.montajDegerAcik) { ImGui::OpenPopup("montajDeger"); bglm.montajDegerAcik = false; } if (ImGui::BeginPopup("montajDeger")) { ImGui::TextUnformatted(bglm.montajDegerAci ? "Açı değeri (derece)" : "Mesafe değeri (mm)"); ImGui::SetNextItemWidth(120); ImGui::InputFloat("##montajDegerGiris", &bglm.montajDegerDeger, 0.0f, 0.0f, "%.2f"); const bool uygulaTiklandi = ImGui::Button("Uygula") || (ImGui::IsItemFocused() && ImGui::IsKeyPressed(ImGuiKey_Enter)); if (uygulaTiklandi && bglm.montajDegerUygula) { const f64 d = bglm.montajDegerAci ? (f64)bglm.montajDegerDeger * (std::numbers::pi / 180.0) : (f64)bglm.montajDegerDeger; bglm.montajDegerUygula(d); ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if (ImGui::Button("İptal")) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } // ---- Hızlı görünümler ---- if (hizliGorunumAcik) { ImGui::OpenPopup("hizliGorunum"); hizliGorunumAcik = false; } if (ImGui::BeginPopup("hizliGorunum")) { struct { const char* ad; HizliGorunum g; } gorunumler[] = { {"Ön", HizliGorunum::On}, {"Arka", HizliGorunum::Arka}, {"Sol", HizliGorunum::Sol}, {"Sağ", HizliGorunum::Sag}, {"Üst", HizliGorunum::Ust}, {"Alt", HizliGorunum::Alt}, {"İzometrik", HizliGorunum::Izometrik}}; for (auto& g : gorunumler) if (ImGui::MenuItem(g.ad)) kamera.gorunumeGit(g.g); ImGui::EndPopup(); } // (Legacy "sahneMenu" dikey popup'ı KALDIRILDI — menuAc hiçbir yerde set // edilmiyordu, popup asla açılmıyordu; sağ-tık bağlamı radyal halka + arama // menüsüyle çizilir. fikatia-dm-arama-menu-akil 🚨 ölü kod temizliği.) // ---- "Hızlı 3D" ağırlık/kütük widget'ı → hizli3d_penceresi.cpp // (hizli3dPencereCiz, davranış birebir). Panel açılışı (long-press + // bağlam menüsü) main.cpp'de kalır; bu çağrı yalnız pencereyi çizer. ---- hizli3dPanel_->ciz(bglm); // ---- Merkezi Ayarlar penceresi (kategorili; AyarSistemi'nden beslenir) ---- ayarlarPanel_->ciz(bglm); // ---- Çökmüş oturum kurtarma teklifi (yalnız açılışta, aday varsa) ---- otoKayitKurtarmaCiz(bglm); // ---- İşlemler (arka plan görev ilerlemesi) ---- // Taşınabilir + kapatılabilir yüzer pencere: başlık + ilerleme çubuğu + iptal. // Veri GorevSistemi'nden atomik okunur (event spam yok, her kare taze). Yeni iş // gelince otomatik geri gelir; X ile kapatılır, Pencereler menüsünden açılır. { const auto isler = cekirdek.gorevler().anlik(); const bool yeniIsGeldi = isler.size() > oncekiIsSayi; // yeni iş → otomatik göster if (yeniIsGeldi) panelIsler = true; oncekiIsSayi = isler.size(); if (panelIsler && !isler.empty()) { // ui::Pencere iskeleti (pencere standardı): pin + yükseklik cap + global skin + // konum kalıcılığı merkezi gelir. İlk açılış sağ-alt köşe (FirstUseEver) birebir; // NoNav/NoFocusOnAppearing korunur (ilerleme penceresi odak çalmaz). // Yeni iş gelince (auto-show) konum HER ZAMAN (Always) tazelenir: kayıtlı // fikocad.pencere.ini konumu eski/farklı bir çözünürlükten kalmışsa (ör. // ikinci monitör sonradan sökülmüş) pencere ekran dışına sabitlenip "hiç // görünmüyor" izlenimi verebiliyordu — kendi kendini onarır. Kullanıcı aynı // oturumda sürükleyip taşırsa, bir sonraki YENİ iş gelene kadar orada kalır. ImGui::SetNextWindowPos({ekranG - 332, ekranY - 150}, yeniIsGeldi ? ImGuiCond_Always : ImGuiCond_FirstUseEver); ui::Pencere pen{"İşlemler", &panelIsler, {.boyut = {320, 118}, .minBoyut = {220, 60}, .pinDurum = &islerUstte, .bayrak = ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoFocusOnAppearing}}; if (pen) { for (const auto& g : isler) { ImGui::PushID((int)g.kimlik); const bool hatali = g.durum == GorevDurum::Hata; const ImVec4 renk = hatali ? ImVec4{1.0f, 0.45f, 0.4f, 1.0f} : ImVec4{0.35f, 0.85f, 0.9f, 1.0f}; // Başlık kırpılır (uzunsa taşmaz), iptal düğmesi sağda sabit. const f32 btnG = 20.0f; ImGui::PushTextWrapPos(ImGui::GetContentRegionAvail().x - btnG); ImGui::TextColored(renk, "%s", g.baslik.c_str()); ImGui::PopTextWrapPos(); ImGui::SameLine(ImGui::GetWindowContentRegionMax().x - btnG + 2); if (ImGui::SmallButton("✕")) cekirdek.gorevler().iptalEt(g.kimlik); char etk[80]; std::snprintf(etk, sizeof(etk), "%.0f%% %s", g.oran * 100.0f, g.mesaj.empty() ? "" : g.mesaj.c_str()); ImGui::ProgressBar(g.oran, ImVec2(-1, 14), etk); // pencere genişliğine uyar ImGui::PopID(); } } } } // ---- Sketcher paneli → sketch_ui.cpp (sketcherPaneliCiz), davranış birebir ---- sketcherPanel_->ciz(bglm); // ---- CATIA "Sketch Tools" SABİT toolbar → sketch_ui.cpp (sketchAraclariCiz) ---- sketchAraclariPanel_->ciz(bglm); // ---- Sahnede sağ-tık bağlam menüsü (duruma göre) → sketch_ui.cpp ---- sketchBaglamMenuCiz(bglm); // ---- CATIA "Sketch Analysis" penceresi (sketchAnalizAcik iken) → sketch_ui.cpp ---- sketchAnalizPenceresiCiz(bglm); // ---- CATIA "Animate Constraint" penceresi (animAcik iken) → sketch_ui.cpp ---- sketchAnimasyonPenceresiCiz(bglm); sketchKisitDegerPenceresiCiz(bglm); // ---- 3B Pah / Kenar Yuvarlatma değer penceresi (CATIA Chamfer/Fillet) ---- if (pah3DAcik) { ImGui::SetNextWindowSize({260, 0}, ImGuiCond_FirstUseEver); ImGui::SetNextWindowPos({ekranG * 0.5f - 130, 96}, ImGuiCond_FirstUseEver); ImGui::SetNextWindowBgAlpha(0.96f); if (ImGui::Begin(pah3DYuvarlat ? "Kenar Yuvarlat (3B)" : "Pah (3B)", &pah3DAcik, ImGuiWindowFlags_NoCollapse)) { // Seçili kenar sayısı (yalnız alt>0 = kenar ögeleri). int kenarSayi = 0; for (const auto& o : sahne.secililer()) if (o.alt > 0) ++kenarSayi; ImGui::TextDisabled("%d kenar seçili", kenarSayi); ImGui::SetNextItemWidth(120); const bool enter = ImGui::InputText(pah3DYuvarlat ? "Yarıçap" : "Mesafe", pah3DGiris, sizeof(pah3DGiris), ImGuiInputTextFlags_EnterReturnsTrue); ImGui::Spacing(); const bool uygula = ImGui::Button("Uygula") || enter; ImGui::SameLine(); if (ImGui::Button("Kapat")) pah3DAcik = false; if (uygula) { const f64 deger = std::strtod(pah3DGiris, nullptr); if (kenarSayi == 0) bilgiVer("Önce kenar seçin (Kenar seçim kipi)"); else if (deger <= 1e-9) bilgiVer("Geçerli bir değer girin"); else { const int n = sahne.pahYuvarlatUygula(deger, pah3DYuvarlat); if (n > 0) { bilgiVer(pah3DYuvarlat ? "Kenar yuvarlatıldı" : "Pah kırıldı"); pah3DAcik = false; } else bilgiVer("İşlem başarısız (kenar/değer geçersiz veya OCCT reddetti)"); } } } ImGui::End(); } // ---- Geç panel batch (polimorfik): CAM Programı → Op → ParçaOp → Takım → // TakımEkseni → Feature → GSD → Analiz → Kesit → Döküm → CAM Sim. // Kayıt sırası (kur()) = eski çağrı sırası birebir. Alt sistem ref'leri: // cekirdek/sahne/camSis/partD/gsdSis/analizSis/refSecim/pencere UygulamaBaglam'dan; // camUstte/dokumSis/vulkan ctor'da bağlı (UygulamaBaglam'da olmayanlar). ---- g_sonAdim = "kareCiz: gec paneller"; // faz izleyici panelYonGec_.cizTumu(bglm); // ---- FikoYT Olcum penceresi -> olcum_penceresi.cpp (kareCizden cikarildi) ---- olcumPenceresiCiz(bglm, olcumUstte); // ---- Durum cubugu -> durum_cubugu.cpp ---- if (!aracKipi) durumCubuguCiz(sahne, zaman, durumMesajBitis, durumMesaj); // ---- Kare çizimi ---- g_sonAdim = "kareCiz: GPU kare"; // faz izleyici VkCommandBuffer kb = vulkan.kareBasla(); if (!kb) { arayuz.kareIptal(); return false; } // kare atlandı (lambda: continue→return) // Pick filtresi zorlaması (refSecim/camSecim/olcum). Mod başlarken kullanıcının // 1/2/3/4 filtresini yedekle, mod bitince geri yükle → filtre sızmaz (O1). const bool pickModAktif = refSecim.aktif() || camSis.secim().aktif() || analizSis.secimAktif() || ort.pushPull.aktif || (olcum.acik && olcum.kip != OlcumKip::Ayarlar); if (pickModAktif && !pickFiltreYedekVar) { pickYedYuz = secimSis.fYuz; pickYedKenar = secimSis.fKenar; pickYedNokta = secimSis.fNokta; pickYedGovde = secimSis.fGovde; pickFiltreYedekVar = true; } else if (!pickModAktif && pickFiltreYedekVar) { secimSis.fYuz = pickYedYuz; secimSis.fKenar = pickYedKenar; secimSis.fNokta = pickYedNokta; secimSis.fGovde = pickYedGovde; pickFiltreYedekVar = false; } // GSD/Hole sahne-ref seçim modu aktifse GPU pick filtresini kilitle // (serbest nokta/yüz → Yüz; kenar ucu → Kenar; köşe → Nokta). En yüksek öncelik. if (refSecim.aktif()) { const int bt = refSecim.beklenenTur(); secimSis.fYuz = (bt == 0 || bt == 1); secimSis.fKenar = (bt == 2); secimSis.fNokta = (bt == 3); secimSis.fGovde = false; } else // Analiz seçim modu (çekim yönü/kesit normali → Yüz, hedef → Gövde). if (analizSis.secimAktif()) { const int bt = analizSis.beklenenVurguTur(); secimSis.fYuz = (bt == 1); secimSis.fKenar = false; secimSis.fNokta = false; secimSis.fGovde = (bt == 0); } else // CAM seçim modu aktifse GPU pick filtresini beklenen topolojiye kilitle // (Kontur/Ada/Düzlem/ToolAxis→Yüz, Kenar→Kenar, Stok→Gövde). Hover doğru // öğeyi vurgular; ölçümden önce gelir (op penceresi öncelikli). if (camSis.secim().aktif()) { const int bt = camSis.secim().beklenenVurguTur(); secimSis.fYuz = (bt == 1); secimSis.fKenar = (bt == 2); secimSis.fNokta = (bt == 3); secimSis.fGovde = (bt == 0); } else // Ölçüm penceresi açıkken GPU pick filtresini aktif seçim filtresinden sür // (Her Şey / Yüzey / Kenar / Nokta) → doğru topoloji türü hover'lanır. if (olcum.acik && olcum.kip != OlcumKip::Ayarlar) { if (olcum.eksenSecModu) { // referans eksen: gövde/datum + kenar/yüz seçilebilsin secimSis.fYuz = true; secimSis.fKenar = true; secimSis.fNokta = true; secimSis.fGovde = true; } else { const SecimFiltre af = olcum.aktifFiltre(); secimSis.fYuz = (af == SecimFiltre::HerSey || af == SecimFiltre::Yuzey); secimSis.fKenar = (af == SecimFiltre::HerSey || af == SecimFiltre::Cizgi); secimSis.fNokta = (af == SecimFiltre::HerSey || af == SecimFiltre::Nokta); secimSis.fGovde = false; } } else // Push/Pull: yalnız YÜZ hover'lanır. Doğrudan Modelleme workbench'inde KENAR da // seçilebilir (kenar fillet/pah gizmosu için) — yüz push/pull + kenar fillet birlikte. if (ort.pushPull.aktif) { const bool dogrudanWB = cekirdek.workbenchler().aktif() == "dogrudan"; secimSis.fYuz = true; secimSis.fKenar = dogrudanWB; secimSis.fNokta = false; secimSis.fGovde = false; } // DM kipinde datum'u pick sırasında ARKAYA al (ayarla): datum seçilebilir kalır ama // model kenar/noktası önce gelir → derinlik-testsiz datum çizgisi model hover'ını çalmaz. secimSis.datumArkada = dogrudanAyar().datumArkada && cekirdek.workbenchler().aktif() == "dogrudan"; // ---- Belge çizimi (belge_ciz.cpp — MDI Dalga 2): hover çözümü + kamera // matrisi + GPU pick geçişi + model/analiz/kenar/nokta/seçim geçişleri. // Ana belge varsayılan viewport hedefine çizer; ek belgeler kendi // ViewportHedef + CizimOnbellek örnekleriyle aynı fonksiyondan geçer. BelgeCizimGirdi cizGirdi; cizGirdi.zaman = zaman; cizGirdi.kare = kare; cizGirdi.imlecX = fare.sonX; cizGirdi.imlecY = fare.sonY; cizGirdi.vpHover = vpHover && !paftaModda; // pafta modunda pick/hover koşmaz // Kamera navigasyonu (döndür/pan/zoom veya tekerlek) sürerken GPU pick'i duraklat. // Tekerlek YALNIZ 3B görünüm hover'lıyken navigasyon sayılır: başka bir ImGui paneli // (Yardım/WTEC/ayarlar) üstünde scroll ederken ana görünümde zoom ghost'u belirmesin. // Gerçek zoom zaten `vpHover && !pusulaUstunde` kapısında (yukarıda) — ghost o kapıyla // aynı hizaya çekildi (07-17: panelde scroll → ana ekranda ghost görünüyordu). const bool tekerNav = (io.MouseWheel != 0.0f) && vpHover && !paftaModda; cizGirdi.navigasyonAktif = (fare.kip >= 0) || tekerNav; // Etkileşim LOD kip-kapısı için aktif navigasyon kipini taşı (0 döndür/1 zoom/2 pan). cizGirdi.navKip = fare.kip; cizGirdi.tekerlekZoom = tekerNav; // B#2: kamera yumuşatma animasyonu oturana kadar da pick'i duraklat (boşuna raster). cizGirdi.kameraHareketli = kamera.hareketliMi(); // Selection Stack "peek" lens: aktif adayın gövdesini sabit imleç dairesinde // occluder'sız göster (merkez=tık noktası, framebuffer-yerel piksel). if (secimYigini.acikMi() && secimGorunum().peekAcik) { if (const SecimAday* pa = secimYigini.aktifAday()) { cizGirdi.peekGovde = pa->govde; cizGirdi.peekAlt = pa->alt; cizGirdi.peekTur = pa->tur; cizGirdi.peekMerkezX = secimYigini.merkez().x - vpRectMinX; cizGirdi.peekMerkezY = secimYigini.merkez().y - vpRectMinY; cizGirdi.peekYari = secimYigini.yaricap(); } } g_sonAdim = "GPU: belgeCiz"; // Araç kipinde ana sahne HİÇ gösterilmiyor → raster + pick tamamen atlanır // (16×16 hedefe boşuna çizim ve her-kare GPU pick yok). Aracın kendi // mini-viewport'u kafaKaydet ile ayrıca kaydedilir. if (!aracKipi) { FIKO_ADIM("belgeCiz (ana sahne kaydı)"); belgeCiz(belge, vulkan.viewportVarsayilan(), vulkan, hatlar, anaCizim, isikDurum, kb, cizGirdi, sonEnBoy, sonG, sonY_); } // Ek belgeler: her açık belge penceresi kendi hedefine (pick + sahne geçişleri). if (!aracKipi) { FIKO_ADIM("belgeleriKaydet (ek pencereler)"); belgeleriKaydet(belgeYon, vulkan, hatlar, isikDurum, kb, zaman, kare); } { FIKO_ADIM("kafaKaydet (kafa panelleri)"); kafaKaydet(vulkan, hatlar, isikDurum, kb, zaman, kare); // kafa panelleri GPU record } // ---- Girdi hover probu (A): çözülmüş hover'ı girdi soyutlamasına besle ---- // Yalnız değişimde HoverDegisti olayı yayar (probun dinleyicisi). Girdi tüketmez. { FIKO_ADIM("hover probu + baglam motoru"); girdi::HoverBilgi hb; hb.govdeId = sahne.vurgu.govde; hb.altId = sahne.vurgu.alt; hb.tur = girdi::SahneHoverProbu::turCevir(secimSis.vurguTur(), sahne.vurgu.govde); { FIKO_ADIM("hoverProbu.besle (HoverDegisti dinleyicileri)"); hoverProbu.besle(hb); } // ---- Bağlam farkındalık motoru (Dalga 1): ham hover + durum → semantik hedef ---- // Motor 18 kategoriyi + durumu (workbench/sketch/araç/seçim) çözer, önbellekli brep // sorgusuyla (tek OCCT taraması) + değişimde BaglamCozuldu yayar. SUNUM YOK // (komut haritası/HUD Dalga 2/3); mevcut politika/menü/hover akışı DEĞİŞMEZ. baglam::BaglamDurum bd; bd.workbench = cekirdek.workbenchler().aktif(); bd.sketchAktif = (aktifSketch != VARLIK_YOK); bd.arac = refSecim.aktif() ? baglam::BaglamDurum::Arac::RefSecim : camSis.secim().aktif() ? baglam::BaglamDurum::Arac::CamSecim : analizSis.secimAktif() ? baglam::BaglamDurum::Arac::Analiz : (olcum.acik && olcum.kip != OlcumKip::Ayarlar) ? baglam::BaglamDurum::Arac::Olcum : baglam::BaglamDurum::Arac::Yok; bd.secimSayi = (u32)sahne.secililer().size(); bd.secimHomojen = bd.secimSayi > 0; // seçim kipi tekil topoloji dayatır switch (sahne.kip()) { case SecimKip::Yuz: bd.secimTuru = girdi::HedefTur::Yuz; break; case SecimKip::Kenar: bd.secimTuru = girdi::HedefTur::Kenar; break; case SecimKip::Nokta: bd.secimTuru = girdi::HedefTur::Nokta; break; default: bd.secimTuru = girdi::HedefTur::Govde; break; } { FIKO_ADIM("baglamMotoru.guncelle"); belge.baglamMotoru.guncelle(hb, bd); } } // ---- Workbench hızlı seçici (sağ-alt FAB + yay). Panellerin üstünde. ---- if (!aracKipi) workbenchSecici(cekirdek); // ---- Uzak politika pompa (kill-switch): bekleyen güncellemeyi uygula + periyodik denetim. // Banner'dan ÖNCE: bu turda engelleme kararı değiştiyse banner doğru durumu çizsin. ---- uzakPolitikaPompa(); // ---- Lisans banner'ı (kalıcı — komut reddi beklemez; toast'tan ÖNCE, aynı katman) ---- lisansBanneriCiz(); // ---- Toast bildirimleri (tüm pencerelerin üstünde, kare-sonu tek çağrı) ---- ui::toastlariCiz(); // ---- Ana geçiş (yalnız ImGui, viewport dokusu içinde gösterilir) ---- g_sonAdim = "GPU: anaGecisBasla"; vulkan.anaGecisBasla(0.04f, 0.045f, 0.06f); g_sonAdim = "GPU: arayuz.ciz (ImGui render)"; arayuz.ciz(kb); g_sonAdim = "GPU: kareBitir"; vulkan.kareBitir(); // Multi-viewport: dışarı taşınan panellerin OS pencereleri (ana submit SONRASI). g_sonAdim = "GPU: viewport pencereleri"; arayuz.viewportPencereleriCiz(); return true; } // ---- FikoBelgePenceresi::kareCiz() sonu ----------------------------------- // ---- FikoBelgePenceresi::baslat() — kare döngüsü öncesi tek-sefer init ----------- void FikoBelgePenceresi::baslat() { const bool senaryoDokum = senaryoDokum_; std::filesystem::path& senaryoDizin = senaryoDizin_; int& snAdim = snAdim_; baslangic_ = std::chrono::steady_clock::now(); oncekiKare_ = baslangic_; kareSuresiMs_ = 0.0f; kare_ = 0; // ---- Görsel oto-senaryo durumu (--senaryo-dokum): kutu → döküm sim → PNG dökümü. // Komutlar registry'den (cekirdek.menuler) çalıştırılır; PNG'ler insa/senaryo/'ya. snAdim = -1; if (senaryoDokum) { wchar_t exeYol[MAX_PATH]{}; GetModuleFileNameW(nullptr, exeYol, MAX_PATH); // insa//uygulama/fikocad/fikocad.exe → insa/senaryo senaryoDizin = std::filesystem::weakly_canonical( std::filesystem::path(exeYol).parent_path() / ".." / ".." / ".." / "senaryo"); std::error_code ec; std::filesystem::create_directories(senaryoDizin, ec); snAdim = 0; gunluk::bilgi("Senaryo modu: PNG dizini {}", senaryoDizin.string()); } } // ---- FikoBelgePenceresi::kareCalistir() — bir kare (zaman + kareCiz + senaryo) --- // return false = bu pencere çıkmak istiyor (oto-test/aygıt-kayıp/senaryo/kapanış). bool FikoBelgePenceresi::kareCalistir() { // Döngü/senaryo gövdesi bare isimlerini korumak için üye alias'ları. Pencere& pencere = pencere_; VulkanBaglam& vulkan = vulkan_; const std::string& shaderDizin = shaderDizin_; BelgeYonetici& belgeYon = belgeYon_; PencereOrtami& ort = ort_; Belge& belge = *ort.belgePtr; UygulamaBaglam& bglm = *ort.bglmPtr; Cekirdek& cekirdek = belge.cekirdek; Sahne& sahne = belge.sahne; DokumSistemi& dokumSis = belge.dokumSis; auto& menuK = cekirdek.menuler(); auto& tumunuSigdir = bglm.tumunuSigdir; f32& simdikiZaman = ort.simdikiZaman; const int otoTestKare = otoTestKare_; std::filesystem::path& senaryoDizin = senaryoDizin_; int& snAdim = snAdim_; f32& snBekle = snBekle_; f32& snTimeout = snTimeout_; u32& snKutu = snKutu_; u32& snSim = snSim_; int& snScrub = snScrub_; (void)pencere; (void)shaderDizin; // Pencere kapandıysa (WM_CLOSE) bu pencere çıkmak ister (döngü sahibi kaldırır). if (pencere_.kapatildi()) return false; { const auto simdi = std::chrono::steady_clock::now(); const f32 zaman = std::chrono::duration(simdi - baslangic_).count(); simdikiZaman = zaman; cekirdek.olaylar().zamanAyarla(zaman); // olay kayıtları uygulama saatini damgalar const f32 hamDt = std::chrono::duration(simdi - oncekiKare_).count(); // dt animasyon/simülasyon için KIRPILIR (büyük sıçrama fiziği patlatır) — davranış aynı. const f32 dt = std::clamp(hamDt, 0.0f, 0.1f); // ÖLÇÜM DÜZELTMESİ 07-21: kare süresi metriği de KIRPILMIŞ dt'den besleniyordu → // gerçek kare 1000 ms sürerken metrik en fazla 100 ms görebiliyor, EWMA ile "53 fps" // raporlanıyordu (ÖLÇÜLDÜ: 6 sn'de 9 kare = ~1.5 fps iken fiko.perf.metrik() 53 dedi). // Donma, kendisini ölçen sayaçta GÖRÜNMÜYORDU; metrik artık HAM deltayı kullanır. kareSuresiMs_ = kareSuresiMs_ * 0.95f + hamDt * 1000.0f * 0.05f; // TEŞHİS: TEK kare takılması (hitch). 600-karelik ortalama loglar bunu gizliyor — // kullanıcı "donuyor" derken sürekli düşük FPS değil, hover/tık anındaki uzun kareyi // hissediyor olabilir. Eşik üstü her kare tek satır (zaman damgası ile komşu alt-sistem // logları — cook/BVH/pick — suçluyu gösterir). Sıcak yolda maliyet: bir karşılaştırma. static u64 hitchKare = 0; ++hitchKare; if (hamDt * 1000.0f > (f32)kareHitchEsigiMs()) { gunluk::uyari("[perf] KARE HITCH {:.0f} ms (kare {})", hamDt * 1000.0f, hitchKare); } oncekiKare_ = simdi; // kareCiz() için kare-yerel zaman durumu (üye üzerinden geçir). dt_ = dt; zaman_ = zaman; simdi_ = simdi; const bool cizildi = kareCiz(); if (cizildi && g_gozcuKalp) g_gozcuKalp->fetch_add(1, std::memory_order_relaxed); // gözcü: KARE tamamlandı // GPU aygıtı kayıp/yanıtsız → pencere çıkmak ister. BU KONTROL ÇİZİMDEN ÖNCE // DEĞERLENDİRİLMELİ: aygıt kaybolunca kareCiz() hep false döner, aşağıdaki // "kare atlandı → devam" erken dönüşü aşağıdaki (satır ~7080) aynı kontrolü ÖLÜ // KODA çeviriyordu → uygulama "temiz kapanisa geciliyor" yazıp SONSUZA KADAR donuk // pencereyle dönüyordu (ölçüldü 07-21: 60 sn sonra hâlâ açık; kullanıcı elle // kapatmak zorunda kalıyordu — "pencerenin içi donuyor" şikayetinin ta kendisi). if (vulkan.aygitKayipMi()) { gunluk::hata("GPU aygiti kayip — uygulama kapatiliyor"); return false; } if (!cizildi) return true; // kare atlandı (swapchain hazır değil) → devam et int& kare = kare_; // senaryo tail sayaç alias'ı (++kare üye üzerinde) // ---- Görsel oto-senaryo (--senaryo-dokum): kare gönderildi → görüntü hazır. // Her adım görseli kurar, sonraki karelerde çizilmesini bekler, PNG döker. if (snAdim >= 0 && zaman >= snBekle) { const auto png = [&](const char* ad) { vulkan.viewportPngKaydet((senaryoDizin / ad).string().c_str()); }; const auto komutCalistir = [&](const char* grup, const char* ad) { for (const auto& o : menuK.hepsi()) if (o.grup == grup && o.ad == ad) { cekirdek.komutCalistir(o, "senaryo"); return; } }; dokum::DokumSimulasyonu* sn = snSim ? dokumSis.bul(snSim) : nullptr; switch (snAdim) { case 0: // kare 10: kutu ekle (60 mm) if (kare < 10) break; komutCalistir("Geometri", "Kutu"); if (!sahne.dugumler().empty()) { Dugum& d = sahne.dugumler().back(); snKutu = d.kimlik; d.p1 = d.p2 = d.p3 = 60.0f; d.yenidenInsaGerek = true; } gunluk::bilgi("Senaryo: kutu eklendi (kimlik {})", snKutu); snAdim = 1; snBekle = zaman + 0.3f; break; case 1: // kamera tümünü sığdır (yumuşatma otursun diye bekle) tumunuSigdir(); snAdim = 2; snBekle = zaman + 1.0f; break; case 2: // sahne PNG + döküm sim ekle + katılaşma çözümü png("01_sahne.png"); snSim = dokumSis.ekle(snKutu); if (dokum::DokumSimulasyonu* s = dokumSis.bul(snSim)) s->cozunurluk = 24; if (!dokumSis.cozumBaslat(snSim)) { gunluk::hata("Senaryo: cozumBaslat basarisiz"); snAdim = 99; break; } gunluk::bilgi("Senaryo: katilasma cozumu basladi (cozunurluk 24)"); snAdim = 3; snTimeout = zaman + 120.0f; break; case 3: // katılaşma bitene kadar bekle → dolum başlat if (dokumSis.cozuluyorMu(snSim)) { if (zaman > snTimeout) { gunluk::hata("Senaryo: cozum TIMEOUT"); snAdim = 99; } break; } if (!sn || !sn->sonuc.hazir) { gunluk::hata("Senaryo: katilasma sonucu yok"); snAdim = 99; break; } gunluk::bilgi("Senaryo: katilasma tamam ({} s)", sn->sonuc.toplamSure); if (!dokumSis.dolumBaslat(snSim)) { gunluk::hata("Senaryo: dolumBaslat basarisiz"); snAdim = 99; break; } snAdim = 4; snTimeout = zaman + 120.0f; break; case 4: // dolum bitene kadar bekle if (dokumSis.cozuluyorMu(snSim)) { if (zaman > snTimeout) { gunluk::hata("Senaryo: dolum TIMEOUT"); snAdim = 99; } break; } if (!sn || !sn->dolum.hazir) { gunluk::hata("Senaryo: dolum sonucu yok"); snAdim = 99; break; } gunluk::bilgi("Senaryo: dolum tamam ({} s)", sn->dolum.toplamSure); snScrub = 0; snAdim = 5; snBekle = zaman + 0.2f; break; case 5: { // scrubber anı: pürüzsüz sıvı yüzeyi görselini kur if (!sn) { snAdim = 99; break; } static const f64 kSnOran[5] = {0.2, 0.4, 0.6, 0.8, 1.0}; const f64 an = kSnOran[snScrub] >= 1.0 ? -1.0 : kSnOran[snScrub] * sn->dolum.toplamSure; dokumSis.dolumGorsellestir(snSim, an, DolumGorunum::Sivi, DolumRenk::Sicaklik); snAdim = 6; snBekle = zaman + 0.4f; // görsel birkaç kare çizilsin break; } case 6: { // scrubber PNG; 5 an bitince katılaşma sıcaklık katmanına geç static const char* kSnAd[5] = {"02_dolum_20.png", "02_dolum_40.png", "02_dolum_60.png", "02_dolum_80.png", "02_dolum_100.png"}; png(kSnAd[snScrub]); if (++snScrub < 5) { snAdim = 5; snBekle = zaman + 0.1f; } else { dokumSis.dolumKaldir(snSim); dokumSis.xrayAyarla(snSim, false, DokumSistemi::XrayHepsi); dokumSis.katmanGorsellestir(snSim, "Sıcaklık (Son)"); snAdim = 7; snBekle = zaman + 0.4f; } break; } case 7: // katılaşma sıcaklık PNG → porozite (varsayılan: baloncuk izo-yüzey) png("03_katilasma_sicaklik.png"); if (sn) { dokumSis.voxelKaldir(snSim); // sıcaklık ısı-haritasını bırak dokumSis.poroziteBaloncukGoster(snSim, true, 0.5f); } snAdim = 8; snBekle = zaman + 0.4f; break; case 8: // porozite PNG → Feedmod katmanı (MAGMA riser doğrulama dili) png("04_porozite.png"); if (sn) { dokumSis.poroziteBaloncukGoster(snSim, false); dokumSis.katmanGorsellestir(snSim, "Feedmod"); } snAdim = 9; snBekle = zaman + 0.4f; break; case 9: { // Feedmod PNG → değer-eşiği taraması ortası ("soyma" yarıda) png("05_feedmod.png"); if (const dokum::SonucKatmanlari* kl = dokumSis.katmanlarAl(snSim)) if (const dokum::KatmanBilgi* fkb = kl->bul("Feedmod")) dokumSis.katmanGorsellestir(snSim, "Feedmod", fkb->min + 0.5f * (fkb->maks - fkb->min)); snAdim = 10; snBekle = zaman + 0.4f; break; } case 10: // eşik tarama PNG → kesit süpürme ortası (X yarı dilim) png("06_esik_tarama.png"); if (sn) { sn->gorselAyar.kesitEksen = 0; sn->gorselAyar.kesitOran = 0.5f; dokumSis.katmanGorsellestir(snSim, "Sıcaklık (Son)"); } snAdim = 11; snBekle = zaman + 0.4f; break; case 11: { // kesit PNG → yalnız-sıvı kipi (katılaşma ortası adacıklar) png("07_kesit.png"); if (sn) { sn->gorselAyar.kesitEksen = -1; sn->gorselAyar.kesitOran = 1.0f; dokumSis.voxelKaldir(snSim); // An: FSTIME yüzdelik ortası (zaman ortası DEĞİL — Al'da Biot küçük, // hücrelerin çoğu son dilimde katılaşır → yarı-zaman hep sıvı görünür). f64 an = 0.5 * sn->sonuc.toplamSure; if (const dokum::SonucKatmanlari* kl = dokumSis.katmanlarAl(snSim)) if (const dokum::KatmanBilgi* skb = kl->bul("Katılaşma Süresi")) an = skb->min + 0.5 * (skb->maks - skb->min); dokumSis.cepheGorsellestir(snSim, an, /*yalnizSivi=*/true); } snAdim = 12; snBekle = zaman + 0.4f; break; } case 12: // yalnız-sıvı PNG → SİLİNDİR (eğri yüzey) taşma doğrulaması png("08_yalniz_sivi.png"); // Eski kutu simini + gövdesini temizle, silindir ekle (r=25, boy=60). if (snSim) { dokumSis.voxelKaldir(snSim); dokumSis.sil(snSim); snSim = 0; } if (snKutu) { sahne.sil(snKutu); snKutu = 0; } komutCalistir("Geometri", "Silindir"); if (!sahne.dugumler().empty()) { Dugum& d = sahne.dugumler().back(); snKutu = d.kimlik; // (silindir kimliğini snKutu'da tut) d.p1 = 25.0f; d.p3 = 60.0f; // yarıçap 25, boy 60 d.yenidenInsaGerek = true; } snAdim = 13; snBekle = zaman + 0.3f; break; case 13: // silindir katılaşma çöz (küp görünüm taşma testi) tumunuSigdir(); snSim = dokumSis.ekle(snKutu); if (dokum::DokumSimulasyonu* s = dokumSis.bul(snSim)) s->cozunurluk = 28; if (!dokumSis.cozumBaslat(snSim)) { snAdim = 99; break; } snAdim = 14; snTimeout = zaman + 120.0f; break; case 14: // çözüm bekle → KÜP görünümü Sıcaklık + PNG (eğri yüzeyde taşma?) if (dokumSis.cozuluyorMu(snSim)) { if (zaman > snTimeout) { snAdim = 99; } break; } dokumSis.katmanGorsellestir(snSim, "Katılaşma Süresi"); // küp mesh snAdim = 15; snBekle = zaman + 0.5f; break; case 15: // silindir küp PNG → GPU SPH dispatch doğrulaması (Vulkan compute) png("09_silindir_kup.png"); // İzole GPU SPH testi: silindir kafesi (case 13-14) zaten kurulu. Döküm // girişi ekle + sphGpuCoz'u DOĞRUDAN çağır (worker/UI'ya dokunmaz; GPU // yoksa hazir=false ile temiz döner). RTX 4090 dispatch süresi loglanır. if (dokum::DokumSimulasyonu* s = dokumSis.bul(snSim)) { dokum::DokumGirisi g; g.konum = {0, 0, 55}; // silindir üst bölgesi (boy 60) g.yon = {0, 0, -1}; g.kutleKg = 0.5; g.sureS = 0.3; g.capMm = 16; s->kurulum.girisler.clear(); s->kurulum.girisler.push_back(g); s->kurulum.graviteYon = {0, 0, -1}; const auto t0 = std::chrono::steady_clock::now(); const dokum::SphDolumSonuc gs = dokum::sphGpuCoz(vulkan, s->kafes, s->kurulum, s->alasim, s->sphAyar); const f64 ms = std::chrono::duration( std::chrono::steady_clock::now() - t0).count(); u32 dolanIc = 0, tumIc = 0; for (u32 c = 0; c < s->kafes.hucreSayi(); ++c) { if (s->kafes.tip[c] != dokum::HucreTip::Ic) continue; ++tumIc; if (c < gs.dolum.varisZamani.size() && gs.dolum.varisZamani[c] >= 0.0) ++dolanIc; } gunluk::bilgi("GPU SPH sonuc: hazir={} partikul={} adim={} maksHiz={:.2f} " "dolum={}/{} kare={} sure={:.0f}ms", gs.hazir, gs.partikulSayi, (unsigned long long)gs.adimSayi, gs.maksHiz, dolanIc, tumIc, gs.kareler.size(), ms); } snAdim = 20; // kullanıcı SPH yolu (partikül bulutu PNG) → sonra MDI snBekle = zaman + 0.3f; break; case 20: // GERÇEK SPH kullanıcı yolu: sphKullan + dolumBaslat // (partikül kaydı + "Partikül bulutu" görseli — çalkantı yolunun // uçtan uca otomatik doğrulaması; case 15 izole sphGpuCoz idi). if (dokum::DokumSimulasyonu* s = dokumSis.bul(snSim); s && s->kafes.gecerliMi()) { s->sphKullan = true; if (!dokumSis.dolumBaslat(snSim)) { gunluk::hata("Senaryo: SPH dolumBaslat basarisiz"); snAdim = 16; break; } snAdim = 21; snTimeout = zaman + 120.0f; } else { snAdim = 16; } break; case 21: // SPH dolumu bekle → partikül bulutu görseli kur if (dokumSis.cozuluyorMu(snSim)) { if (zaman > snTimeout) { gunluk::hata("Senaryo: SPH TIMEOUT"); snAdim = 16; } break; } if (sn && sn->dolum.hazir) { const size_t sonPartikul = sn->sph.kareler.empty() ? 0 : sn->sph.kareler.back().partikul.size(); gunluk::bilgi("Senaryo SPH: kare={} sonKarePartikul={} maksHiz={:.2f}", sn->sph.kareler.size(), sonPartikul, sn->sph.maksHiz); if (sonPartikul == 0) gunluk::hata("Senaryo SPH: partikul kaydi BOS (bulut cizilemez)"); dokumSis.dolumGorsellestir(snSim, 0.6 * sn->dolum.toplamSure, DolumGorunum::Partikul, DolumRenk::Sicaklik); snAdim = 22; snBekle = zaman + 0.5f; } else { gunluk::hata("Senaryo: SPH dolum sonucu yok"); snAdim = 16; } break; case 22: // partikül bulutu PNG → MDI smoke devam png("09b_sph_partikul.png"); dokumSis.dolumKaldir(snSim); snAdim = 16; snBekle = zaman + 0.2f; break; case 16: { // MDI SMOKE: 2. belge aç + kendi sahnesine kutu ekle BelgeKaydi& bk = belgeYon.yeniBelge(vulkan, shaderDizin); bk.belge->cekirdek.uygula( std::make_unique(bk.belge->sahne, DugumTip::Kutu)); if (!bk.belge->sahne.dugumler().empty()) { Dugum& d = bk.belge->sahne.dugumler().back(); d.p1 = d.p2 = d.p3 = 60.0f; d.yenidenInsaGerek = true; } bk.belge->kamera.sigdir({0, 0, 30}, 90.0f); gunluk::bilgi("Senaryo MDI: 2. belge acildi (kimlik {}, {} dugum)", bk.kimlik, bk.belge->sahne.dugumler().size()); snAdim = 17; snBekle = zaman + 1.0f; // pencere boyutlansın + kamera otursun break; } case 17: // MDI SMOKE: her iki belgenin hedefinden PNG (bağımsız içerik) png("10_mdi_ana_belge.png"); if (!belgeYon.hepsi().empty()) { BelgeKaydi& bk = *belgeYon.hepsi().back(); const std::string yol = (senaryoDizin / "10_mdi_belge2.png").string(); if (bk.hedef->pngKaydet(yol.c_str())) gunluk::bilgi("Senaryo MDI: belge-2 PNG yazildi ({}x{})", bk.hedef->boyut().width, bk.hedef->boyut().height); else gunluk::hata("Senaryo MDI: belge-2 PNG YAZILAMADI"); } gunluk::bilgi("Senaryo tamam — PNG dizini: {}", senaryoDizin.string()); snAdim = 99; break; default: break; } if (snAdim == 99) return false; // temiz kapanış (senaryo tamam → pencere çık) } ++kare; if (otoTestKare > 0 && kare >= otoTestKare) { gunluk::bilgi("Oto-test tamam: {} kare çizildi ({})", kare, vulkan.aygitAdi()); return false; // oto-test bitti → pencere çık } // GPU aygıtı kayıp/yanıtsız → çık (daha fazla GPU çağrısı yapma, temiz kapan). if (vulkan.aygitKayipMi()) { gunluk::hata("GPU aygiti kayip — uygulama kapatiliyor"); return false; } } return true; // normal kare → devam et } } namespace fiko { // Pencere surucusu disari acilir sarmali (anon pencereDongusu ic; wWinMain cagirir). int calistir(int otoTestKare, bool senaryoDokum) { return pencereDongusu(otoTestKare, senaryoDokum); } } // namespace fiko