#include "cam_simulasyon_penceresi.hpp" #include "fiko/core/birim_bicim.hpp" // global birim küsüratı (Ayarlar → Birimler) #include #include #include #include #include #include #include #include #include "fiko/cam/cam_islem.hpp" #include "fiko/cam/cam_sistemi.hpp" #include "fiko/cam/part_operasyon.hpp" #include "fiko/cam/takim.hpp" #include "fiko/cam/takim_yolu.hpp" #include "belge_ciz.hpp" // perfGosterge() — sim ölçümleri (Perf HUD; tahmin değil) #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/core/gunluk.hpp" // bir-kerelik kurulum ms'sini fikocad.log'a yaz #include "fiko/core/mat/vec.hpp" #include "fiko/kutuk/dexel3_gpu_motor.hpp" #include "fiko/kutuk/dexel3_motor.hpp" // dynamic_cast: span birleştirme/cap sayaçları #include "fiko/kutuk/kutuk_motor.hpp" #include "fiko/kutuk/sdf_gpu_motor.hpp" #include "fiko/render/mesh_veri.hpp" #include "fiko/render/orbit_kamera.hpp" // 3B nokta-pick: ekran→dünya izdüşümü #include "fiko/render/vertex.hpp" // KenarVertex (sahne yol düğümü) + Vertex (takım mesh) #include "fiko/sahne/sahne.hpp" #include "cam_parca_op_penceresi.hpp" // camParcaOpPencereAc — çizimden kütük seç editörü #include "kutuk_async.hpp" // arka plan gerçek-zamanlı kütük carve (worker thread) #include "sim_ses.hpp" // prosedürel işleme sesi (winmm) #include "uygulama_baglam.hpp" // kamera + sahne/cam erişimi (pick için) #include "fiko/ui/bilesen.hpp" #include "fiko/ui/form.hpp" // ikonSeritBasla/Bitir — Araçlar popup ikon grupları #include "fiko/ui/pencere_cerceve.hpp" #include "fiko/ui/tema.hpp" #include "fiko/ui/web_skin.hpp" // ortak teal cam skin (kafa pencereleriyle paylaşılır) #include "imgui.h" #include "pencere_util.hpp" namespace fiko { namespace { using cam::CamIslem; using cam::CamSistemi; using cam::Hamle; using cam::HamleTip; using cam::TakimYolu; // Hareket türü — timeline + renk kaynağı (blueprint §1/§8). enum class HareketTuru : u8 { Isleme, Yaklasma, Uzaklasma, Bosta }; const char* hareketAdi(HareketTuru t) { switch (t) { case HareketTuru::Isleme: return "İşleme"; case HareketTuru::Yaklasma: return "Yaklaşma"; case HareketTuru::Uzaklasma: return "Uzaklaşma"; case HareketTuru::Bosta: return "Boşta"; } return "—"; } // Timeline segment / gösterge rengi (yeşil/sarı/mavi/kırmızı). ImU32 hareketRenk(HareketTuru t) { switch (t) { case HareketTuru::Isleme: return IM_COL32(70, 210, 110, 255); // yeşil case HareketTuru::Yaklasma: return IM_COL32(235, 205, 70, 255); // sarı case HareketTuru::Uzaklasma: return IM_COL32(80, 160, 240, 255); // mavi case HareketTuru::Bosta: return IM_COL32(230, 90, 80, 255); // kırmızı } return IM_COL32(140, 140, 140, 255); } // Sahne yol düğümü rengi (timeline'la aynı) — kenar düğümü tek renk taşır, o yüzden // hareket türü başına ayrı düğüm (web renkIsleme/Yaklasma/Uzaklasma/Bosta eşleniği). Vec3f hareketRenkVec(HareketTuru t) { switch (t) { case HareketTuru::Isleme: return {0.27f, 0.82f, 0.43f}; // yeşil case HareketTuru::Yaklasma: return {0.92f, 0.80f, 0.27f}; // sarı case HareketTuru::Uzaklasma: return {0.31f, 0.63f, 0.94f}; // mavi case HareketTuru::Bosta: return {0.90f, 0.35f, 0.31f}; // kırmızı } return {0.55f, 0.55f, 0.55f}; } // ---- Simülasyon nokta modeli (blueprint §1) ------------------------------- struct SimNokta { Vec3d p{}; // dünya konum (mm) f64 hiz = 0; // ilerleme (mm/dk) f64 devir = 0; // rpm HareketTuru tur = HareketTuru::Bosta; u32 takimNo = 0; Vec3d takimYonu{0, 0, 1}; f64 guncelSure = 0; // bu noktaya kadar kümülatif süre (dk) f64 toplamSure = 0; // programın toplam süresi (dk) — her noktada aynı u32 opId = 0; // kaynak operasyon kimliği }; struct OpSinir { u32 opId = 0; std::string ad; int baslangicIdx = 0; // bu op'un ilk noktası (nokta dizinine) }; struct SimProgram { std::vector noktalar; std::vector sinirlar; // operasyon başlangıç indeksleri f64 toplamSure = 0; // dk bool demo = false; // gerçek yol yok, demo üretildi mi }; // HamleTip → HareketTuru eşleme (Hızlı=Yaklaşma/Uzaklaşma bağlama göre; burada // Hızlı→Boşta değil, Z yönüne göre karar: yön yukarı ise Uzaklaşma, aşağı ise // Yaklaşma; yatay hızlı → Boşta). HareketTuru hamleTur(const Hamle& h, const Vec3d& oncekiP) { switch (h.tip) { case HamleTip::Kesme: return HareketTuru::Isleme; case HamleTip::Dalma: return HareketTuru::Yaklasma; case HamleTip::Hizli: { const f64 dz = h.nokta.z - oncekiP.z; const f64 yatay = std::hypot(h.nokta.x - oncekiP.x, h.nokta.y - oncekiP.y); if (dz > 0.05 && std::abs(dz) > yatay) return HareketTuru::Uzaklasma; if (dz < -0.05 && std::abs(dz) > yatay) return HareketTuru::Yaklasma; return HareketTuru::Bosta; } } return HareketTuru::Bosta; } // Bir TakimYolu'ndan SimNokta üretir; kümülatif süreyi (dk) ilerletir. // Süre = mesafe / (hız mm/dk). hız==0 (Hızlı) → sabit 5000 mm/dk varsayımı. // SIFIR SÜRE GUARD: hız<=0 ve hamle Hızlı değilse konumlama hızına düşer. void yoldanUret(const TakimYolu& yol, u32 opId, u32 takimNo, f64 devir, const Vec3d& takimYonu, SimProgram& prog, f64& sureBirikim) { if (yol.hamleler.empty()) return; Vec3d oncekiP = yol.hamleler.front().nokta; for (size_t i = 0; i < yol.hamleler.size(); ++i) { const Hamle& h = yol.hamleler[i]; const HareketTuru tur = (i == 0) ? HareketTuru::Bosta : hamleTur(h, oncekiP); if (i > 0) { f64 mesafe = h.nokta.mesafe(oncekiP); if (!std::isfinite(mesafe) || mesafe < 0.0) mesafe = 0.0; // NaN/negatif guard f64 hizMmDk = (h.tip == HamleTip::Hizli) ? 5000.0 : h.ilerleme; // sıfır/negatif/NaN süre bölme guard (NaN<1.0 false → ayrıca isfinite şart). if (!std::isfinite(hizMmDk) || hizMmDk < 1.0) hizMmDk = 5000.0; sureBirikim += mesafe / hizMmDk; // dk } SimNokta n; n.p = h.nokta; n.hiz = (h.tip == HamleTip::Hizli) ? 5000.0 : h.ilerleme; n.devir = devir; n.tur = tur; n.takimNo = takimNo; n.takimYonu = takimYonu; n.guncelSure = sureBirikim; n.opId = opId; prog.noktalar.push_back(n); oncekiP = h.nokta; } } // Görünür demo yol (gerçek CL yolu yokken pencere çalışsın diye). Farklı hareket // türleri içerir: güvenli inme (Yaklaşma) → dörtgen kesme (İşleme) → çıkış (Uzaklaşma). void demoYolUret(SimProgram& prog) { prog.demo = true; TakimYolu yol; const f64 ilerleme = 1200.0, dalma = 400.0; auto ekle = [&](HamleTip t, f64 x, f64 y, f64 z, f64 f) { yol.hamleler.push_back({t, Vec3d{x, y, z}, f}); }; // Başlangıç güvenli yükseklikte. ekle(HamleTip::Hizli, -30, -30, 20, 0); ekle(HamleTip::Hizli, -30, -30, 2, 0); // konumlama (Boşta) ekle(HamleTip::Dalma, -30, -30, -3, dalma); // dalma (Yaklaşma) // Dörtgen kesme (İşleme). ekle(HamleTip::Kesme, 30, -30, -3, ilerleme); ekle(HamleTip::Kesme, 30, 30, -3, ilerleme); ekle(HamleTip::Kesme, -30, 30, -3, ilerleme); ekle(HamleTip::Kesme, -30, -30, -3, ilerleme); // İkinci pas biraz derin. ekle(HamleTip::Dalma, -30, -30, -6, dalma); ekle(HamleTip::Kesme, 30, -30, -6, ilerleme); ekle(HamleTip::Kesme, 30, 30, -6, ilerleme); ekle(HamleTip::Kesme, -30, 30, -6, ilerleme); ekle(HamleTip::Kesme, -30, -30, -6, ilerleme); // Çıkış (Uzaklaşma). ekle(HamleTip::Hizli, -30, -30, 20, 0); yol.istatistikGuncelle(); f64 sure = 0; prog.sinirlar.push_back({0, "Demo Yol", 0}); yoldanUret(yol, 0, 1, 6000.0, Vec3d{0, 0, 1}, prog, sure); } // CamSistemi'nin yollarından programı kur. // opKimlik==0 → tüm etkin CamIslem + CamOperasyon yolları ardışık. // opKimlik>0 → yalnız o CATIA op'un yolu (hesaplandıysa); yoksa demo yola düşer. SimProgram programKur(CamSistemi& cam, u32 opKimlik) { SimProgram prog; f64 sure = 0; // Büyük yollarda (200k+ hamle) realloc jank'ını önle: toplamı sayıp tek seferde ayır. { size_t toplam = 0; for (const CamIslem& op : cam.hepsi()) if (op.etkin && (opKimlik == 0)) toplam += op.yol.hamleler.size(); for (const cam::CamOperasyon& op : cam.operasyonlar()) if (!op.bastirilmis && (opKimlik == 0 || op.kimlik == opKimlik)) toplam += op.yol.hamleler.size(); prog.noktalar.reserve(toplam + 8); } if (opKimlik == 0) { for (CamIslem& op : cam.hepsi()) { if (!op.etkin || op.yol.hamleler.size() < 2) continue; prog.sinirlar.push_back({op.kimlik, op.ad.empty() ? cam::islemTipAdi(op.tip) : op.ad, (int)prog.noktalar.size()}); yoldanUret(op.yol, op.kimlik, (u32)op.takimNo, op.devir, Vec3d{0, 0, 1}, prog, sure); } for (cam::CamOperasyon& op : cam.operasyonlar()) { if (op.bastirilmis || op.yol.hamleler.size() < 2) continue; prog.sinirlar.push_back({op.kimlik, op.ad.empty() ? cam::islemTipCatiaAdi(op.tip) : op.ad, (int)prog.noktalar.size()}); yoldanUret(op.yol, op.kimlik, (u32)op.takimId, op.hizlar.devir, op.takimYonu.yon, prog, sure); } } else if (cam::CamOperasyon* op = cam.opBul(opKimlik)) { // Tek CATIA op replay: yol hesaplandıysa onu kullan. if (op->yol.hamleler.size() >= 2) { prog.sinirlar.push_back({op->kimlik, op->ad.empty() ? cam::islemTipCatiaAdi(op->tip) : op->ad, (int)prog.noktalar.size()}); yoldanUret(op->yol, op->kimlik, (u32)op->takimId, op->hizlar.devir, op->takimYonu.yon, prog, sure); } } if (prog.noktalar.size() < 2) { // gerçek yol yok → görünür demo (sınır durumu guard) prog.noktalar.clear(); prog.sinirlar.clear(); sure = 0; demoYolUret(prog); } // Toplam süreyi tüm noktalara yaz (gösterge %'si için). prog.toplamSure = prog.noktalar.empty() ? 0 : prog.noktalar.back().guncelSure; for (SimNokta& n : prog.noktalar) n.toplamSure = prog.toplamSure; return prog; } // ---- Pencere durumu (tekil; UI-only, kalıcı değil) ------------------------ bool g_simAcik = false; bool g_simPin = false; bool g_motorPencereAcik = false; // "Simülasyon Motoru" AYRI pencere (web referansı) bool g_motorPencerePin = false; u32 g_simOpKimlik = 0; SimProgram g_prog; int g_poz = 0; // aktif nokta indeksi [0..noktalar.size()-1] int g_yonu = 0; // -1 geri, 0 dur, +1 ileri int g_simHizi = 50; // nokta/sn tabanı (1-1000) int g_reelIlerleme = 100; // % (1-200) reel zaman çarpanı f64 g_zamanBirikim = 0; // kesirli poz birikimi (DeltaTime ile) // ---- Sahne görselleştirme (web fiko-similasyon.component eşleniği) --------- // Web'de similasyonCizgisi (setDrawRange ile açığa çıkan renkli yol) + aktifTakim // (poz'a giden yönlenmiş takım) sahnede gösterilirdi; native sim yalnız timeline/ // gösterge taşıyordu → "APT simüle edilmiyor" hissi. Burada yolu + hareket eden // takımı 3B sahneye taşıyoruz. // PERF MİMARİSİ (web setDrawRange eşleniği): yol BİR KEZ GPU'ya yüklenir; poz değişince // YALNIZ çizim aralığı (drawRange, O(1)) ayarlanır. AYRI "tüm yol" düğümü YOK — eskiden // her kare 2M vertex çiziliyordu (kilitlenmenin kökü). İki kip: // • tek çizgi (g_yolTekDugum): tek line-list; reveal=[2·pozMin,2·poz) → prefix GEREKMEZ. // • renkli (g_yolGecilenDugum[4]): hareket türü başına düğüm (kenar düğümü tek-renk // taşır → bucket başı kovaOnek prefix). YALNIZ küçük yolda (g_renkEsik altı). Varlik g_simGrup = VARLIK_YOK; // yol + takım düğümlerini toplayan grup u32 g_yolTekDugum = 0; // tek line-list (büyük yol; O(1) reveal, prefix'siz) u32 g_yolGecilenDugum[4] = {0, 0, 0, 0}; // renkli mod: hareket türü başına düğüm u32 g_takimDugum = 0; // hareket eden takım gövdesi (hazirMeshGuncelle) u32 g_ucDugum = 0; // glide uç çizgisi: node[poz]→interpolasyon (çizgi takım ucunda biter) bool g_renkliMod = false; // aktif kurulum renkli mi (küçük) yoksa tek çizgi mi int g_sonCizilenPoz = -1; // yol/takım yenilemeyi poz'a kapıla (her-kare O(N) yok) int g_sonCizilenMin = -1; // pozMin değişimini de yakala (reveal başlangıcı) bool g_sahneGoster = true; // sahnede yol/takım göster (Ayarlar → CAM; canlı checkbox) bool g_yolGoster = true; // YALNIZ yol (çizgi + uç + noktalar) — takımdan bağımsız bool g_yolGosterCache = true; // değişimi yakala → reveal'i bir kez yeniden uygula bool g_takimGoster = true; // YALNIZ takım gövdesi — yoldan bağımsız bool g_takimGosterCache = true; // değişimi yakala → düğümü bir kez kur/sil // Tüm yolu göster: reveal [pozMin,poz) yerine yolun TAMAMI çizilir. KAPALI (varsayılan) // = klasik ilerlemeli açılma (yol oynatıldıkça çizilir). Ayar: cam.sim_tum_yol. bool g_tumYol = false; bool g_tumYolCache = false; bool g_tekRenk = false; // geçilen yolu tek renk (cyan) çiz; kapalı = hareket-türü rengi bool g_tekRenkCache = false; // tek/çoklu renk geçişini yakala → düğümleri renkle yeniden kur // Bu segment sayısı üstünde tek-çizgi (perf); Ayarlar → CAM. 150k idi ama tipik CATIA // APT'si 200k+ segment (ölçüldü: gerçek dosya 223969) → renkler kayboluyordu. Renkli // modun EK maliyeti sınırlı: vertex sayısı AYNI (her segment tek kovaya girer), fark // 1 yerine 4 düğüm + kovaOnek prefix (N×16B; 224k'da ~3.6 MB). Kurulum süresi // fikocad.log'daki "[sim] kur: N nokta, X ms, kip=renkli-kova" satırından ÖLÇÜLÜR. u32 g_renkEsik = 1000000; bool g_cizgiUzerinde = true; // takım çizgi üzerinde interpolasyonla kayar (web aktifTakimTasima); // kapalı = noktadan noktaya zıpla (eski davranış) bool g_cizgiOnde = false; // yol çizgileri derinliksiz üstte (web simCizgiOnde) bool g_cizgiOndeCache = false; // değişimi yakala → düğüm bayraklarını O(1) güncelle bool g_noktaGoster = false; // yol noktalarını (vertex) göster (web "Noktaları Göster") u32 g_noktaDugum = 0; // yol noktaları düğümü (0 = yok; toggle'da kur/sil) bool g_ses = false; // işleme sesi (winmm procedural — sim_ses) bool g_kameraTakip = false; // kamera odağı hareketli takımı izler std::vector> g_kovaOnek; // renkli mod prefix (küçük yol; bellek küçük) // Oynatma aralığı (web pozMin/pozMax): 3B nokta-pick veya timeline ile daraltılır. int g_pozMin = 0; int g_pozMax = -1; // -1 = son nokta // 3B nokta-pick kipi: -1 kapalı, 0 başlangıç, 1 şimdiki, 2 bitiş seçimi bekliyor. int g_pickHedef = -1; // Kaçma (runout) göstergesi — takım profili işleme eksenine göre ekstremleri. // g_kacma: web'deki TEK (otomatik seçilmiş) işaretli değer — geriye dönük uyumluluk. // g_kacmaMin/g_kacmaMax: HER eksenin HER İKİ ucu (−yönü/+yönü) — tezgahçı takımı // hangi taraftan (X+/X−/Y+/Y−/Z+/Z−) parçaya değdirdiyse DOĞRU işaretli kaçma // değerini (temas noktası → takım ucu/küre merkezi ofseti) buradan okur; tek // otomatik-seçilmiş değer (g_kacma) hangi tarafın seçildiğini göstermediği için // ters taraftan dokunulduğunda işaret hatalı yorumlanabilir. Vec3d g_kacma{}; Vec3d g_kacmaMin{}, g_kacmaMax{}; // [minX,minY,minZ] / [maxX,maxY,maxZ] — ham ekstremler u32 g_kacmaTakim = ~0u; // hangi takım/yön için hesaplandı (cache) Vec3d g_kacmaYon{0, 0, 1}; // ============================================================================ // KÜTÜK (stock removal) SİMÜLASYONU — motor/kutuk bağlantısı (blueprint §8) // ---------------------------------------------------------------------------- // Aktif SimProgram'ın SimNokta'larından takimUygula/sweptUygula ile malzeme oyar. // Kip fabrikası (kutukMotoruYap / GPU varyant), batch replay (kare başına N hamle), // on-demand mesh çıkarma → sahne düğümü (hazirMeshEkle; NaN sınır guard). // ============================================================================ using cam::PartOperasyon; using kutuk::KutukSimKip; using kutuk::KutukSimMotoru; using kutuk::TakimGeo; struct KutukDurum { std::unique_ptr motor; int kip = 2; // 0 Dexel3, 1 ZBuffer6, 2 SDF_MC (VARSAYILAN), 3 TriDexel Pro // (profesyonel analitik keskin — Vericut sınıfı, kip seçilerek) bool gpu = false; // GPU varyant denesin mi f32 cozunurluk = 1.0f; // hücre boyu (mm) bool kuruldu = false; int oyulanPoz = 0; // kütüğe kadar işlenen SimNokta indeksi int hamlePerKare = 256; // batch üst sınırı (zaman bütçesi de var — hangisi önce) bool otoOy = true; // ▶ Sim ile Oyma aktif mi (replay ile senkron) — VARSAYILAN AÇIK int hedefIstek = -1; // "Sona Kadar Oy": kare-kare bu poza ilerle (-1 = yok) u32 meshDugum = 0; // sahnedeki kütük mesh düğümü (0 = yok) bool meshBayat = false; // oyma yapıldı, mesh yenilenmeli f64 hacimMm3 = 0; // kaldırılan hacim (gösterge) u32 sonMeshVert = 0; // son çıkarılan kütük mesh vertex sayısı (ÖLÇÜM; donma teşhisi) f32 gercekCoz = 0; // GERÇEK hücre boyu (mm) — cap kabalaştırması sonrası (radius teşhisi) int sonLogPoz = -1; // son log'landığı oyulanPoz (carve trajektorisi log throttle) Aabbf sinir; // kurulu stok sınırı (takım uzatma "tavan" fix'i için) std::string log; // Türkçe durum/hata satırı Vec3d sonNokta{}; // önceki işlenen nokta (swept başlangıcı) bool sonNoktaVar = false; }; KutukDurum g_kutuk; // Kütük perf ayarları (AyarSistemi "CAM"): batch zaman bütçesi + mesh yenileme aralığı. // Eski davranış: sabit 64 hamle/kare + HER KARE tam mesh çıkarma + sil/ekle → kasma. f32 g_kutukBatchMs = 5.0f; // kare başına oyma bütçesi (ms) f32 g_kutukMeshMs = 100.0f; // mesh çıkarma en az bu aralıkla (ms; ~10 Hz) u32 g_kutukMaksHucre = 300; // en uzun eksende maks hücre (grid patlama koruması; ÖLÇÜLDÜ). // 512 denendi → DC seri tam-grid + 20M voxel → kütük gelmiyordu. // Keskin için cell yerine analitik Hermite normal kullanılıyor. bool g_kutukTakimUzat = true; // takım katısını tip'ten stok üstüne uzat (derin dalışta // "tavan"/2-yüzey kalmasın). Kapalı = gerçek kesici boyu. bool g_kutukKeskin = false; // Dual Contouring: keskin kenar/köşe korunur (MC yuvarlar). // Cache'siz tam grid (yavaş; kalite görünümü). SDF+MC kipinde. std::chrono::steady_clock::time_point g_kutukSonMesh{}; // son mesh çıkarma zamanı // ASYNC carve (worker thread; kutuk_async): oyma ana thread'i bloklamaz → oynatma // akıcı. Motor GPU değilse ve tercih açıksa devreye girer (GPU Vulkan submit main-only, // worker'da yasak → GPU'da eski senkron yol). Grid worker'a devredilir; g_kutuk.motor // null olur (sync yol doğal olarak atlanır). Davranış korunur: kapalı = eski senkron yol. KutukAsyncCarve g_kutukAsync; bool g_kutukAsyncTercih = true; // AyarSistemi cam.kutuk_async (vars. açık; gerçek-zamanlı) KutukSimKip kutukKipEnum(int i) { switch (i) { case 1: return KutukSimKip::ZBuffer6; case 2: return KutukSimKip::SDF_MC; case 3: return KutukSimKip::TriDexel; default: return KutukSimKip::Dexel3; } } // Yolun tüm noktalarından dünya-uzayı AABB (kütük fallback sınırı için). Aabbf yolSiniri(const SimProgram& prog) { Aabbf s; for (const SimNokta& n : prog.noktalar) if (std::isfinite(n.p.x) && std::isfinite(n.p.y) && std::isfinite(n.p.z)) s.genislet({(f32)n.p.x, (f32)n.p.y, (f32)n.p.z}); return s; } // Kesme/dalma (malzeme KALDIRAN) hamlelerin AABB'si; her kesme segmentinin iki ucu. // Rapid/home/uzaklaşma noktalarını DIŞLAR — yoksa tool-change / G28-home gibi uzak // konumlar stok sınırını devasa şişirir (ÖLÇÜLDÜ fikocad.log: enUzun=2026mm → cap // 6.75mm hücre → takım altı çözünürlük → süpürme kaldırılamaz). Kesme yoksa tüm-nokta. Aabbf yolKesmeSiniri(const SimProgram& prog) { Aabbf s; bool var = false; auto ekle = [&](const Vec3d& p) { if (std::isfinite(p.x) && std::isfinite(p.y) && std::isfinite(p.z)) s.genislet({(f32)p.x, (f32)p.y, (f32)p.z}); }; for (size_t i = 1; i < prog.noktalar.size(); ++i) { const SimNokta& n = prog.noktalar[i]; if (n.tur != HareketTuru::Isleme && n.tur != HareketTuru::Yaklasma) continue; ekle(prog.noktalar[i - 1].p); // sweep başı ekle(n.p); // sweep sonu var = true; } return var ? s : yolSiniri(prog); } // Aktif PartOperasyon'un stok bbox'ı (dünya). Yoksa yol bbox + pay döner. // Geçersiz/dejenerе → gecerliMi()==false döner (çağıran guard eder). Aabbf kutukSiniriBelirle(cam::CamSistemi& cam, const SimProgram& prog) { if (PartOperasyon* pop = cam.parcaOpBul((u32)cam.aktifParcaOp)) { const cam::Stok& st = pop->stok; Aabbf s; if (st.tip == cam::StokTip::Silindir) { const f32 r = (f32)st.boyut.x * 0.5f, h = (f32)st.boyut.z; s.asgari = {(f32)st.origin.x - r, (f32)st.origin.y - r, (f32)st.origin.z}; s.azami = {(f32)st.origin.x + r, (f32)st.origin.y + r, (f32)st.origin.z + h}; } else { // Kutu / GovdeOffset → kutu yaklaşımı s.asgari = {(f32)st.origin.x, (f32)st.origin.y, (f32)st.origin.z}; s.azami = {(f32)(st.origin.x + st.boyut.x), (f32)(st.origin.y + st.boyut.y), (f32)(st.origin.z + st.boyut.z)}; } if (s.gecerliMi() && s.kosegen() > 1e-3f) return s; } // Fallback: KESME bölgesi bbox + pay (rapid/home dışlanır → sınır parçaya oturur). Aabbf s = yolKesmeSiniri(prog); if (!s.gecerliMi()) return s; // çağıran guard eder const f32 pay = 8.0f; s.asgari = {s.asgari.x - pay, s.asgari.y - pay, s.asgari.z - pay}; s.azami = {s.azami.x + pay, s.azami.y + pay, s.azami.z + pay}; return s; } // Sim çizim fonksiyonundan verilen Vulkan bağlamı (GPU kütük varyantı için). VulkanBaglam* g_kutukVb = nullptr; // Kütük simülasyon kipini KAPAT: async carve dur, sahne mesh'i sil, motoru bırak. // (Kütük butonu aç/kapa toggle'ı — ikinci tık kütüğü tamamen kaldırır.) void kutukKapat(Sahne& sahne) { g_kutukAsync.durdur(); if (g_kutuk.meshDugum) { sahne.sil(g_kutuk.meshDugum); g_kutuk.meshDugum = 0; } g_kutuk.motor.reset(); g_kutuk.kuruldu = false; g_kutuk.meshBayat = false; g_kutuk.oyulanPoz = 0; g_kutuk.sonNoktaVar = false; g_kutuk.hedefIstek = -1; g_kutuk.hacimMm3 = 0; g_kutuk.sonMeshVert = 0; g_kutuk.sonLogPoz = -1; g_kutuk.log = "Kütük simülasyonu kapalı"; } // Kütük motorunu (yeniden) kur: fabrika + kip + GPU denemesi + stok/sınır. void kutukKur(cam::CamSistemi& cam) { g_kutukAsync.durdur(); // varsa eski async carve'i kapat (motor yeniden yaratılıyor) const KutukSimKip kip = kutukKipEnum(g_kutuk.kip); // GPU varyant yalnız Dexel3/SDF için var; ZBuffer6 CPU-only. std::unique_ptr motor; if (g_kutuk.gpu && g_kutukVb) { if (kip == KutukSimKip::Dexel3) motor = kutuk::kutukMotoruYapGpu(*g_kutukVb); else if (kip == KutukSimKip::SDF_MC) motor = kutuk::kutukMotoruYapSdfGpu(*g_kutukVb); } if (!motor) { if (g_kutuk.gpu && (kip == KutukSimKip::Dexel3 || kip == KutukSimKip::SDF_MC)) g_kutuk.log = "GPU hazır değil — CPU kütük motoruna düşüldü"; motor = kutuk::kutukMotoruYap(kip); } if (!motor) { g_kutuk.log = "Bu kip henüz gelmedi (motor yok)"; g_kutuk.kuruldu = false; return; } const Aabbf sinir = kutukSiniriBelirle(cam, g_prog); if (!sinir.gecerliMi()) { // geçersiz bbox guard (NaN fit'i önler) g_kutuk.log = "Kütük sınırı belirlenemedi (stok yok + yol boş)"; g_kutuk.kuruldu = false; return; } cam::Stok stok; if (PartOperasyon* pop = cam.parcaOpBul((u32)cam.aktifParcaOp)) stok = pop->stok; f64 coz = g_kutuk.cozunurluk; if (!(coz > 1e-3)) coz = 1.0; // sıfır/negatif çözünürlük guard // GRID PATLAMA KORUMASI (ÖLÇÜLDÜ 07-19 fikocad.log: 1mm çöz.+büyük stok → ~28M // vertexli mesh → 1.34 GB GpuTampon → donma). En uzun eksende hücre sayısını // g_kutukMaksHucre ile kapla; gerekirse çöz. kabalaşır (kullanıcı uyarılır). const Vec3f boyut = sinir.azami - sinir.asgari; const f64 enUzun = std::max({(f64)boyut.x, (f64)boyut.y, (f64)boyut.z, 1e-3}); const u32 maksH = std::max(8u, g_kutukMaksHucre); const f64 cozMin = enUzun / (f64)maksH; const bool kabalandi = coz < cozMin; if (kabalandi) coz = cozMin; const u32 hucreEnUzun = (u32)(enUzun / coz + 0.5); // Programda kullanılan en küçük takım çapı — hücre bundan kabaysa ince kesim kaçar. f64 minCap = 1e9; for (const SimNokta& n : g_prog.noktalar) if (const cam::Takim* t = cam.takimBul((int)n.takimNo)) if (t->cap > 1e-3 && t->cap < minCap) minCap = t->cap; if (minCap > 1e8) minCap = 0.0; const bool hucreKaba = minCap > 1e-3 && coz > minCap * 0.5; // takım < 2 hücre → kaçar const char* kipAd[] = {"Dexel3", "ZBuffer6", "SDF+MC", "TriDexelPro"}; const char* ka = kipAd[g_kutuk.kip < 4 ? g_kutuk.kip : 0]; motor->keskinMeshAta(g_kutukKeskin); // kur'dan ÖNCE → norm_ (analitik Hermite) ayrılsın const bool kurOk = motor->kutukKur(stok, sinir, coz); gunluk::bilgi("[sim] kütük kur[{}]{}: enUzun={:.1f}mm çöz={:.3f}mm → {} hücre/eksen{}; " "min takım Ø={:.2f}mm{}", ka, g_kutuk.gpu ? "-GPU" : "", enUzun, coz, hucreEnUzun, kabalandi ? " (KAPLANDI)" : "", minCap, !kurOk ? " ✖ KURULAMADI" : hucreKaba ? " ⚠ HÜCRE TAKIMDAN KABA (ince kesim kaçar)" : ""); if (!kurOk) { g_kutuk.log = "Kütük kurulamadı (geçersiz stok/sınır veya bellek bütçesi)"; g_kutuk.kuruldu = false; return; } g_kutuk.motor = std::move(motor); g_kutuk.motor->keskinMeshAta(g_kutukKeskin); // DC tercihi (sync yol için) g_kutuk.gercekCoz = (f32)coz; // gerçek hücre boyu (cap sonrası) — radius teşhisi g_kutuk.kuruldu = true; g_kutuk.oyulanPoz = 0; g_kutuk.hacimMm3 = 0; g_kutuk.sinir = sinir; // takım-uzatma "tavan" fix'i stok üstünü buradan okur g_kutuk.meshBayat = true; g_kutuk.sonNoktaVar = false; g_kutuk.sonLogPoz = -1; // carve trajektorisi loglaması yeniden başlasın if (kabalandi) g_kutuk.log = "Çözünürlük " + std::to_string(coz).substr(0, 5) + "mm'e kabalaştırıldı (grid patlamasını önlemek için; Ayarlar → " "CAM 'Kütük maks hücre')"; else if (g_kutuk.log.empty()) g_kutuk.log = "Kütük kuruldu (" + std::to_string(hucreEnUzun) + " hücre/eksen)"; } // Aktif SimNokta'nın takımından TakimGeo üret (cam::Takim → TakimGeo). TakimGeo takimGeoNoktadan(cam::CamSistemi& cam, const SimNokta& n) { TakimGeo tg; // takım yoksa varsayılan Ø10 düz if (const cam::Takim* t = cam.takimBul((int)n.takimNo)) tg = kutuk::takimGeoYap(*t); if (n.takimYonu.uzunlukKare() > 1e-9) tg.eksen = n.takimYonu.birim(); // SEÇENEK (g_kutukTakimUzat): kesici katıyı tip'ten stok üstüne uzat. Kesici boyu // (ör. 50mm) < dalış derinliği (ör. 363mm mold) olunca tip'in üstünde KESİLMEMİŞ // "tavan" kalıyordu → "2 yüzey / oymuyor iterek gidiyor" (ÖLÇÜLDÜ+Fikri gözlemi). // Takım ekseni yönünde stok en-uzak noktasına kadar uzatınca üst temizlenir. // Görsel takım mesh'i (simTakimMesh) ETKİLENMEZ — o ayrı profilden çizilir. if (g_kutukTakimUzat && g_kutuk.sinir.gecerliMi()) { const Vec3d e = tg.eksen; const Aabbf& s = g_kutuk.sinir; f64 ulas = 0.0; for (int c = 0; c < 8; ++c) { const Vec3d k{(c & 1) ? (f64)s.azami.x : (f64)s.asgari.x, (c & 2) ? (f64)s.azami.y : (f64)s.asgari.y, (c & 4) ? (f64)s.azami.z : (f64)s.asgari.z}; ulas = std::max(ulas, (k - n.p).nokta(e)); // +eksen yönünde en uzak stok mesafesi } if (ulas > tg.boy) tg.boy = ulas + tg.cap; // stok üstünü aş → üst kapak tam kessin } return tg; } // Batch oyma: g_kutuk.oyulanPoz → hedef arası SimNokta'ları işle. Kare başına // ZAMAN BÜTÇESİ (g_kutukBatchMs) + hamle üst sınırı — hangisi önce dolarsa // (eski sabit 64/kare büyük yolda sonsuz sürüyor, küçük bütçesizlikte kasıyordu). // İşleme/dalma hamlelerini uygular; boşta/hızlı hamleler malzeme kaldırmaz ama // takım konumunu ilerletir (sonNokta). hedef = replay pozisyonu (senkron) veya son. void kutukBatchOy(cam::CamSistemi& cam, int hedef) { if (!g_kutuk.kuruldu || !g_kutuk.motor) return; const int son = (int)g_prog.noktalar.size() - 1; if (hedef > son) hedef = son; if (hedef <= g_kutuk.oyulanPoz) return; const auto baslangic = std::chrono::steady_clock::now(); const auto butce = std::chrono::microseconds((long long)(g_kutukBatchMs * 1000.0f)); int islenen = 0; while (g_kutuk.oyulanPoz < hedef && islenen < g_kutuk.hamlePerKare && (islenen % 16 != 15 || // chrono çağrısını 16 hamlede bir yap (ucuz döngü) std::chrono::steady_clock::now() - baslangic < butce)) { const int i = g_kutuk.oyulanPoz + 1; const SimNokta& n = g_prog.noktalar[(size_t)i]; const TakimGeo tg = takimGeoNoktadan(cam, n); if (g_kutuk.sonNoktaVar) { // İşleme/dalma → süpürme kesim; hızlı/boşta → yalnız konum ilerlet. if (n.tur == HareketTuru::Isleme || n.tur == HareketTuru::Yaklasma) g_kutuk.motor->sweptUygula(g_kutuk.sonNokta, n.p, tg); } else { g_kutuk.motor->takimUygula(n.p, tg); } g_kutuk.sonNokta = n.p; g_kutuk.sonNoktaVar = true; g_kutuk.oyulanPoz = i; ++islenen; } g_kutuk.hacimMm3 = g_kutuk.motor->kaldirilanHacimMm3(); if (islenen > 0) g_kutuk.meshBayat = true; } // Kütük mesh'ini sahneye çıkar/güncelle (on-demand; her kare DEĞİL). // Geçersiz/boş mesh → düğüm eklenmez (NaN kamera fit gotcha). void kutukMeshYenile(Sahne& sahne) { if (!g_kutuk.kuruldu || !g_kutuk.motor) return; kutuk::MeshVeri km; g_kutuk.motor->meshCikar(km); if (km.vertexSayi() == 0 || !km.sinir.gecerliMi()) { // Boş/dejenerе mesh → varsa eski düğümü kaldır (görsel tutarlılık). if (g_kutuk.meshDugum) { sahne.sil(g_kutuk.meshDugum); g_kutuk.meshDugum = 0; } g_kutuk.meshBayat = false; return; } // kutuk::MeshVeri (düz float) → render MeshVeri (Vertex). Gri stok rengi. const Vec3f gri{0.62f, 0.64f, 0.68f}; MeshVeri veri; const u32 vsay = km.vertexSayi(); veri.vertexler.reserve(vsay); for (u32 i = 0; i < vsay; ++i) { Vertex v; v.konum[0] = km.pozisyon[i * 3 + 0]; v.konum[1] = km.pozisyon[i * 3 + 1]; v.konum[2] = km.pozisyon[i * 3 + 2]; v.normal[0] = km.normal[i * 3 + 0]; v.normal[1] = km.normal[i * 3 + 1]; v.normal[2] = km.normal[i * 3 + 2]; v.renk[0] = gri.x; v.renk[1] = gri.y; v.renk[2] = gri.z; v.altKimlik = 0; veri.vertexler.push_back(v); } veri.indeksler = km.indeks; veri.sinir = km.sinir; g_kutuk.sonMeshVert = vsay; // ÖLÇÜM: mesh ne kadar büyük (donma teşhisi/doğrulama) // Carve trajektorisi: ilk çıkarım + her ~500 hamlede bir log (hacim artıyor mu?). if (g_kutuk.sonLogPoz < 0 || g_kutuk.oyulanPoz - g_kutuk.sonLogPoz >= 500 || (g_kutuk.hacimMm3 > 0 && g_kutuk.sonLogPoz == 0)) { g_kutuk.sonLogPoz = g_kutuk.oyulanPoz; const char* kipAd[] = {"Dexel3", "ZBuffer6", "SDF+MC", "TriDexelPro"}; gunluk::bilgi("[sim] kütük mesh[{}]: {} vertex, hacim={:.1f} mm³, işlenen={}/{}", kipAd[g_kutuk.kip < 4 ? g_kutuk.kip : 0], vsay, g_kutuk.hacimMm3, g_kutuk.oyulanPoz, (int)g_prog.noktalar.size() - 1); } // Yerinde güncelle (ping-pong memcpy — döküm scrubber kalıbı): canlı oymada her // yenilemede buffer create/BVH kurulumu/sil-ekle churn'ü KASIYORDU. Düşerse ekle. if (!(g_kutuk.meshDugum && sahne.hazirMeshGuncelle(g_kutuk.meshDugum, veri))) { if (g_kutuk.meshDugum) sahne.sil(g_kutuk.meshDugum); g_kutuk.meshDugum = sahne.hazirMeshEkle("Kütük Simülasyon", VARLIK_YOK, gri, std::move(veri)); } g_kutuk.meshBayat = false; g_kutukSonMesh = std::chrono::steady_clock::now(); } // ---- ASYNC carve köprüsü (worker thread; kutuk_async) --------------------- // Worker'a verilecek nokta snapshot'ı: p + kesme bayrağı + ÖNCEDEN çözülmüş TakimGeo // (takım-uzat dahil). Böylece worker cam takım tablosuna/g_prog'a hiç dokunmaz. std::vector karveNoktalariKur(cam::CamSistemi& cam) { std::vector v; v.reserve(g_prog.noktalar.size()); for (const SimNokta& n : g_prog.noktalar) { KarveNokta k; k.p = n.p; k.kesme = (n.tur == HareketTuru::Isleme || n.tur == HareketTuru::Yaklasma); k.tg = takimGeoNoktadan(cam, n); v.push_back(k); } return v; } // Motoru async worker'a devret + carve başlat. GPU motorunda ÇAĞRILMAZ (Vulkan // submit worker'da yasak). Sonrası: g_kutuk.motor null, ilerleme async'ten okunur. void kutukAsyncBaslat(cam::CamSistemi& cam) { if (!g_kutuk.kuruldu || !g_kutuk.motor || g_kutuk.gpu) return; g_kutukAsync.baslat(std::move(g_kutuk.motor), karveNoktalariKur(cam), g_kutukBatchMs, g_kutukMeshMs, g_kutuk.kip); g_kutukAsync.keskinAta(g_kutukKeskin); // Dual Contouring tercihi worker'a } // Worker'ın ürettiği render MeshVeri'yi sahneye yükle (GPU upload — MAIN-THREAD). // kutukMeshYenile'nin yalnız GPU-upload kısmı; dönüşüm zaten worker'da yapıldı. void kutukAsyncMeshYukle(Sahne& sahne, MeshVeri veri) { if (veri.vertexler.empty() || !veri.sinir.gecerliMi()) { if (g_kutuk.meshDugum) { sahne.sil(g_kutuk.meshDugum); g_kutuk.meshDugum = 0; } return; } g_kutuk.sonMeshVert = (u32)veri.vertexler.size(); const Vec3f gri{0.62f, 0.64f, 0.68f}; if (!(g_kutuk.meshDugum && sahne.hazirMeshGuncelle(g_kutuk.meshDugum, veri))) { if (g_kutuk.meshDugum) sahne.sil(g_kutuk.meshDugum); g_kutuk.meshDugum = sahne.hazirMeshEkle("Kütük Simülasyon", VARLIK_YOK, gri, std::move(veri)); } } // Oyulan kütüğü .obj olarak yaz (teşhis): pozisyon + normal + yüz. AV throwaway // exe'yi engellediğinden ölçümü gerçek fikocad'dan alıyoruz (normal/geometri incelemesi). bool kutukObjYaz(const std::string& yol) { if (!g_kutuk.kuruldu || !g_kutuk.motor) return false; kutuk::MeshVeri km; g_kutuk.motor->meshCikar(km); const u32 vs = km.vertexSayi(); if (vs == 0) return false; std::FILE* f = std::fopen(yol.c_str(), "wb"); if (!f) return false; for (u32 i = 0; i < vs; ++i) std::fprintf(f, "v %.3f %.3f %.3f\n", km.pozisyon[i * 3], km.pozisyon[i * 3 + 1], km.pozisyon[i * 3 + 2]); for (u32 i = 0; i < vs; ++i) std::fprintf(f, "vn %.4f %.4f %.4f\n", km.normal[i * 3], km.normal[i * 3 + 1], km.normal[i * 3 + 2]); const u32 ts = (u32)km.indeks.size() / 3; for (u32 t = 0; t < ts; ++t) { const u32 a = km.indeks[t * 3] + 1, b = km.indeks[t * 3 + 1] + 1, c = km.indeks[t * 3 + 2] + 1; std::fprintf(f, "f %u//%u %u//%u %u//%u\n", a, a, b, b, c, c); } std::fclose(f); gunluk::bilgi("[sim] kütük OBJ yazıldı: {} ({} vertex, {} ucgen, hacim={:.0f}mm³)", yol, vs, ts, g_kutuk.motor->kaldirilanHacimMm3()); return true; } void kutukSifirla(Sahne& sahne) { if (g_kutukAsync.calisiyorMu()) g_kutukAsync.sifirlaVeHedef(0); // worker grid'i sıfırla else if (g_kutuk.motor) g_kutuk.motor->sifirla(); g_kutuk.oyulanPoz = 0; g_kutuk.hacimMm3 = 0; g_kutuk.sonNoktaVar = false; g_kutuk.meshBayat = true; if (g_kutuk.meshDugum) { sahne.sil(g_kutuk.meshDugum); g_kutuk.meshDugum = 0; } g_kutuk.log = "Kütük sıfırlandı"; } // Etkin oynatma aralığı [pozMin, pozMax] (web pozMin/pozMax): pick veya timeline // ile daraltılır; g_pozMax=-1 → son nokta. int pozMaxEtkin() { const int son = (int)g_prog.noktalar.size() - 1; if (son < 0) return 0; return (g_pozMax < 0 || g_pozMax > son) ? son : g_pozMax; } void pozKilitle() { const int son = (int)g_prog.noktalar.size() - 1; if (son < 0) { g_poz = 0; g_pozMin = 0; g_pozMax = -1; return; } if (g_pozMin < 0) g_pozMin = 0; if (g_pozMin > son) g_pozMin = son; if (g_pozMax >= 0 && g_pozMax < g_pozMin) g_pozMax = g_pozMin; const int maxE = pozMaxEtkin(); if (g_poz < g_pozMin) g_poz = g_pozMin; if (g_poz > maxE) g_poz = maxE; } // ---- Sahne görselleştirme yardımcıları ------------------------------------ // noktalar[bas..son] ardışık çiftlerinden LINE_LIST kenar üret (dünya uzayı). void simYolKenar(int bas, int son, std::vector& out, Aabbf& sinir) { out.clear(); if (son <= bas) return; out.reserve((size_t)(son - bas) * 2); for (int i = bas + 1; i <= son; ++i) { const Vec3d& a = g_prog.noktalar[(size_t)(i - 1)].p; const Vec3d& b = g_prog.noktalar[(size_t)i].p; const Vec3f af{(f32)a.x, (f32)a.y, (f32)a.z}; const Vec3f bf{(f32)b.x, (f32)b.y, (f32)b.z}; out.push_back({{af.x, af.y, af.z}, 0}); out.push_back({{bf.x, bf.y, bf.z}, 0}); sinir.genislet(af); sinir.genislet(bf); } } // Takım profili: eksen boyunca (h yükseklik, r yarıçap) — h=0 kesim ucu. Tip'e göre // gerçek kesici silueti + sap/tutucu (web FikoTakimFrezeleme eşleniği). holder=true // noktalar gri tutucu (döner kısım). Sonra revolve ile katı mesh olur. struct ProfilNok { f64 h; f64 r; bool holder; }; void takimProfilKur(const cam::Takim* t, std::vector& pr) { constexpr f64 PI = 3.14159265358979; f64 R = 3.0, boy = 24.0, kose = 0.0, ucAci = 118.0, sapCap = 0.0; cam::TakimTip tip = cam::TakimTip::ParmakFreze; if (t) { if (t->cap > 1e-3) R = t->cap * 0.5; boy = t->boy > 1e-3 ? t->boy : R * 6.0; tip = t->tip; kose = t->koseYaricap; ucAci = t->ucAci; sapCap = t->sapCap; } pr.clear(); const int arc = 6; auto add = [&](f64 h, f64 r, bool hold = false) { pr.push_back({h, r, hold}); }; switch (tip) { case cam::TakimTip::KureselFreze: // yarımküre uç for (int i = 0; i <= arc; ++i) { const f64 a = (PI * 0.5) * i / arc; add(R - R * std::cos(a), R * std::sin(a)); } break; case cam::TakimTip::TorusFreze: { // düz taban + köşe radyusu const f64 rc = (kose > 1e-3 && kose < R) ? kose : R * 0.2; add(0, R - rc); for (int i = 1; i <= arc; ++i) { const f64 a = (PI * 0.5) * i / arc; add(rc - rc * std::cos(a), (R - rc) + rc * std::sin(a)); } break; } case cam::TakimTip::KonikFreze: case cam::TakimTip::Matkap: case cam::TakimTip::PuntaMatkap: case cam::TakimTip::CenterDrill: { // konik uç (uç açısı) const f64 yari = ((ucAci > 1 && ucAci < 179) ? ucAci : 118.0) * 0.5 * PI / 180.0; add(0, 0); add(R / std::tan(yari), R); break; } default: // ParmakFreze + diğerleri: düz uç add(0, R); break; } if (pr.back().r < R - 1e-6) add(pr.back().h, R); // gövdeye düz geçiş add(boy, R); // kesici gövde tepesi const f64 hr = (sapCap > 1e-3 && sapCap * 0.5 > R) ? sapCap * 0.5 : R * 1.6; // tutucu yarıçapı add(boy, hr, true); // omuz add(boy + std::max(boy * 0.8, R * 4.0), hr, true); // tutucu tepesi } // Sadece KESME UCU geometrisini içeren profil (gövde/sap/tutucu YOK) — kaçma // hesabı için. takimProfilKur'un aksine gövde tepesi (h=boy) ve tutucu (omuz + // tutucu tepesi, r=hr>R olabilir) noktalarını EKLEMEZ: onlar temas/kesme yapmaz, // dahil edilirse (özellikle eğik B/C ekseninde, hatta dik eksende tutucu R'den // büyükse) kaçma X/Y/Z değerlerini gerçekte olmayan biçimde şişirir. void takimUcProfilKur(const cam::Takim* t, std::vector& pr) { constexpr f64 PI = 3.14159265358979; f64 R = 3.0, kose = 0.0, ucAci = 118.0; cam::TakimTip tip = cam::TakimTip::ParmakFreze; if (t) { if (t->cap > 1e-3) R = t->cap * 0.5; tip = t->tip; kose = t->koseYaricap; ucAci = t->ucAci; } pr.clear(); const int arc = 6; auto add = [&](f64 h, f64 r) { pr.push_back({h, r, false}); }; switch (tip) { case cam::TakimTip::KureselFreze: for (int i = 0; i <= arc; ++i) { const f64 a = (PI * 0.5) * i / arc; add(R - R * std::cos(a), R * std::sin(a)); } break; case cam::TakimTip::TorusFreze: { const f64 rc = (kose > 1e-3 && kose < R) ? kose : R * 0.2; add(0, R - rc); for (int i = 1; i <= arc; ++i) { const f64 a = (PI * 0.5) * i / arc; add(rc - rc * std::cos(a), (R - rc) + rc * std::sin(a)); } break; } case cam::TakimTip::KonikFreze: case cam::TakimTip::Matkap: case cam::TakimTip::PuntaMatkap: case cam::TakimTip::CenterDrill: { const f64 yari = ((ucAci > 1 && ucAci < 179) ? ucAci : 118.0) * 0.5 * PI / 180.0; add(0, 0); add(R / std::tan(yari), R); break; } default: // ParmakFreze + diğerleri: düz uç add(0, R); break; } } // Takım profilini eksende döndürerek (surface of revolution) dünya-uzayı katı mesh // üret. Dünya vertex → hazirMeshGuncelle donuşumu KORUR (döküm scrubber kalıbı, jank yok). void simTakimMesh(const Vec3d& merkez, Vec3d eksen, const cam::Takim* t, MeshVeri& veri) { constexpr f64 PI = 3.14159265358979; std::vector pr; takimProfilKur(t, pr); if (eksen.uzunlukKare() < 1e-9) eksen = {0, 0, 1}; eksen = eksen.birim(); const Vec3d u = (std::abs(eksen.z) < 0.9 ? eksen.capraz({0, 0, 1}) : eksen.capraz({1, 0, 0})).birim(); const Vec3d v = eksen.capraz(u).birim(); const int seg = 24; const Vec3f toolRenk{1.0f, 0.55f, 0.10f}, holderRenk{0.55f, 0.57f, 0.60f}; veri.vertexler.clear(); veri.indeksler.clear(); auto ekleHalka = [&](const ProfilNok& p) -> u32 { const u32 base = (u32)veri.vertexler.size(); const Vec3f c = p.holder ? holderRenk : toolRenk; for (int i = 0; i < seg; ++i) { const f64 a = 2.0 * PI * i / seg; const Vec3d yon = u * std::cos(a) + v * std::sin(a); const Vec3d wp = merkez + eksen * p.h + yon * p.r; const Vec3d n = (p.r > 1e-6) ? yon : (eksen * -1.0); Vertex vv{}; vv.konum[0] = (f32)wp.x; vv.konum[1] = (f32)wp.y; vv.konum[2] = (f32)wp.z; vv.normal[0] = (f32)n.x; vv.normal[1] = (f32)n.y; vv.normal[2] = (f32)n.z; vv.renk[0] = c.x; vv.renk[1] = c.y; vv.renk[2] = c.z; veri.vertexler.push_back(vv); } return base; }; u32 alt = ekleHalka(pr[0]); for (size_t k = 1; k < pr.size(); ++k) { const u32 ust = ekleHalka(pr[k]); for (int i = 0; i < seg; ++i) { const u32 a0 = alt + (u32)i, a1 = alt + (u32)((i + 1) % seg); const u32 b0 = ust + (u32)i, b1 = ust + (u32)((i + 1) % seg); veri.indeksler.insert(veri.indeksler.end(), {a0, b0, b1, a0, b1, a1}); } alt = ust; } veri.sinir = Aabbf{}; for (const Vertex& vv : veri.vertexler) veri.sinir.genislet({vv.konum[0], vv.konum[1], vv.konum[2]}); } void simSahneTemizle(Sahne& sahne) { if (g_yolTekDugum) { sahne.sil(g_yolTekDugum); g_yolTekDugum = 0; } for (u32& d : g_yolGecilenDugum) if (d) { sahne.sil(d); d = 0; } if (g_takimDugum) { sahne.sil(g_takimDugum); g_takimDugum = 0; } if (g_ucDugum) { sahne.sil(g_ucDugum); g_ucDugum = 0; } if (g_noktaDugum) { sahne.sil(g_noktaDugum); g_noktaDugum = 0; } if (g_simGrup) { sahne.sil(g_simGrup); g_simGrup = VARLIK_YOK; } g_kovaOnek.clear(); g_kovaOnek.shrink_to_fit(); // büyük yol prefix'ini gerçekten serbest bırak g_sonCizilenPoz = -1; g_sonCizilenMin = -1; } // Yol noktaları düğümü (web "Noktaları Göster"): tüm CL noktalarını beyaz vertex // olarak kur. Toggle'da kur/sil (kalıcı gizli düğüm görünürlük-cache riskinden kaçınır). void simNoktaDugumKur(Sahne& sahne) { if (g_noktaDugum || !g_simGrup || g_prog.noktalar.size() < 1) return; NoktaVeri nv; nv.vertexler.reserve(g_prog.noktalar.size()); Aabbf sinir; for (const SimNokta& n : g_prog.noktalar) { const Vec3f p{(f32)n.p.x, (f32)n.p.y, (f32)n.p.z}; nv.vertexler.push_back({{p.x, p.y, p.z}, 0}); sinir.genislet(p); } nv.noktaSayi = (u32)nv.vertexler.size(); MeshVeri bos; bos.sinir = sinir; // frustum cull doğru olsun (boş mesh sentinel'i değil) g_noktaDugum = sahne.hazirMeshEkle("Yol Noktaları", g_simGrup, Vec3f{1, 1, 1}, bos, {}, std::move(nv)); } // "Çizgi önde" bayrağını AKTİF yol düğüm(ler)ine uygula. void simCizgiOndeUygula(Sahne& sahne, bool ustte) { if (g_yolTekDugum) sahne.kenarNesneUstte(g_yolTekDugum, ustte); for (u32 d : g_yolGecilenDugum) if (d) sahne.kenarNesneUstte(d, ustte); } // Program kurulunca: yolu BİR KEZ GPU'ya yükle (tek çizgi VEYA renkli kova). Sonra // reveal her kare kenarNesneCizAralik (O(1)) — web setDrawRange eşleniği. Büyük yol // (>g_renkEsik segment) → tek line-list (renk yerine PERF; bellek/çizim minimum). void simSahneKur(Sahne& sahne) { simSahneTemizle(sahne); const size_t N = g_prog.noktalar.size(); if (!g_sahneGoster || N < 2) return; g_simGrup = sahne.grupEkle("🎬 Simülasyon", VARLIK_YOK); // Uç çizgi düğümü KURULUMDA oluştur (render setine girer → mid-frame kenarNesneEkle // entegrasyon riski yok). Başta gizli (drawRange 0); glide'da güncellenip gösterilir. { std::vector d = {{{0.0f, 0.0f, 0.0f}, 0}, {{0.0f, 0.0f, 0.0f}, 0}}; Aabbf ds; ds.genislet({0.0f, 0.0f, 0.0f}); g_ucDugum = sahne.kenarNesneEkle("Uç", g_simGrup, Vec3f{0.15f, 0.95f, 1.0f}, d, ds); sahne.kenarNesneCizAralik(g_ucDugum, 0, 0); } const size_t seg = N - 1; g_renkliMod = !g_tekRenk && seg <= (size_t)g_renkEsik; // küçük + çoklu-renk istendi if (!g_renkliMod) { // ---- Tek çizgi (lean): tüm segmentler tek line-list; reveal = [2·poz aralığı) ---- std::vector kenar; Aabbf sinir; simYolKenar(0, (int)N - 1, kenar, sinir); if (!kenar.empty() && sinir.gecerliMi()) { g_yolTekDugum = sahne.kenarNesneEkle("Yol", g_simGrup, Vec3f{0.10f, 0.85f, 1.0f}, kenar, sinir); sahne.kenarNesneCizAralik(g_yolTekDugum, 0, 0); // reveal 0'dan } } else { // ---- Renkli: hareket türü başına kova + prefix (yalnız küçük yol) ---- std::vector kova[4]; g_kovaOnek.assign(N, {0, 0, 0, 0}); for (size_t i = 1; i < N; ++i) { const Vec3d& a = g_prog.noktalar[i - 1].p; const Vec3d& b = g_prog.noktalar[i].p; int bi = (int)g_prog.noktalar[i].tur; if (bi < 0 || bi > 3) bi = 3; kova[bi].push_back({{(f32)a.x, (f32)a.y, (f32)a.z}, 0}); kova[bi].push_back({{(f32)b.x, (f32)b.y, (f32)b.z}, 0}); for (int b2 = 0; b2 < 4; ++b2) g_kovaOnek[i][b2] = (u32)kova[b2].size(); } for (int b = 0; b < 4; ++b) { if (kova[b].empty()) continue; Aabbf bs; for (const KenarVertex& kv : kova[b]) bs.genislet({kv.konum[0], kv.konum[1], kv.konum[2]}); g_yolGecilenDugum[b] = sahne.kenarNesneEkle("Yol", g_simGrup, hareketRenkVec((HareketTuru)b), kova[b], bs); sahne.kenarNesneCizAralik(g_yolGecilenDugum[b], 0, 0); } } g_tekRenkCache = g_tekRenk; if (g_cizgiOnde) simCizgiOndeUygula(sahne, true); g_cizgiOndeCache = g_cizgiOnde; g_sonCizilenPoz = -1; // reveal + takım ilk karede kurulur g_sonCizilenMin = -1; // "Tüm yol" açıkken çizim aralığını KURULUMDA aç: yol, simSahneGuncelle'nin // poz-değişti kapısına (ve pencere katlı/oynatma durumuna) bağlı kalmadan ANINDA // görünür. Aksi halde poz=0'da aralık 0 kalıp "yol gözükmüyor" oluyordu. if (g_tumYol && g_yolGoster) { const u32 sonV = (u32)(N - 1); if (g_yolTekDugum) sahne.kenarNesneCizAralik(g_yolTekDugum, 0, 2u * sonV); else if (!g_kovaOnek.empty()) for (int b = 0; b < 4; ++b) if (g_yolGecilenDugum[b]) sahne.kenarNesneCizAralik(g_yolGecilenDugum[b], 0, g_kovaOnek[(size_t)sonV][b]); } g_tumYolCache = g_tumYol; } // Kaçma (runout) — takım profil halkalarının işleme eksenine göre GERÇEK // (işaretli) min/max ekstremleri. Her halka (h, r) için o eksendeki // projeksiyon aralığı [h·e_i − r·√(1−e_i²), h·e_i + r·√(1−e_i²)]'dir; TÜM // halkalar üzerinden GLOBAL min ve max alınır — bu, halkaların birleşiminin // (takımın tam hacminin) o eksendeki gerçek min/max'ıdır ve mesh tabanlı // convex-hull ekstremiyle birebir eşleşir. Web referansı // (fiko-similasyon.component.ts, _kacmaDegerleriAl) ile AYNI seçim // sezgiselliği uygulanır: |min| > çap ise max, değilse min raporlanır // (İŞARETLİ tek değer, üç eksen için ayrı ayrı). // NOT: önceki sürüm |e·h| + r·sin ifadesinin TÜM halkalar üzerindeki // maksimumunu alıyordu — bu HER ZAMAN pozitif bir bounding-box // yarı-genişliğiydi; referansın ürettiği işaretli uç-nokta koordinatıyla // aynı büyüklük DEĞİLDİ, bu yüzden genel açılarda referanstan sapıyordu. void kacmaHesapla(const cam::Takim* t, const Vec3d& eksenHam) { Vec3d e = eksenHam; if (e.uzunlukKare() < 1e-9) e = {0, 0, 1}; e = e.birim(); std::vector pr; takimUcProfilKur(t, pr); // yalnız kesme ucu — gövde/sap/tutucu YOK (bkz. yorum) const f64 cap = (t && t->cap > 1e-3) ? t->cap : 6.0; // web: aktifNokta.takim.cap const f64 sx = std::sqrt(std::max(0.0, 1.0 - e.x * e.x)); const f64 sy = std::sqrt(std::max(0.0, 1.0 - e.y * e.y)); const f64 sz = std::sqrt(std::max(0.0, 1.0 - e.z * e.z)); f64 minX = 0, maxX = 0, minY = 0, maxY = 0, minZ = 0, maxZ = 0; bool ilk = true; for (const ProfilNok& p : pr) { const f64 cx = e.x * p.h, cy = e.y * p.h, cz = e.z * p.h; const f64 loX = cx - p.r * sx, hiX = cx + p.r * sx; const f64 loY = cy - p.r * sy, hiY = cy + p.r * sy; const f64 loZ = cz - p.r * sz, hiZ = cz + p.r * sz; if (ilk) { minX = loX; maxX = hiX; minY = loY; maxY = hiY; minZ = loZ; maxZ = hiZ; ilk = false; } else { minX = std::min(minX, loX); maxX = std::max(maxX, hiX); minY = std::min(minY, loY); maxY = std::max(maxY, hiY); minZ = std::min(minZ, loZ); maxZ = std::max(maxZ, hiZ); } } // FİZİKSEL SINIR: kaçma, temas noktasından takım eksenine/ucuna olan RADYAL // ofsettir — takım eğik (B/C açılı) olduğunda gövdenin uzak (h büyük) noktaları // eksene projekte edilince yarıçapı aşan (kol-boyu × açı karışımı) değerler // üretebilir; bu matematiksel olarak "doğru" ama fiziksel temas noktası hiçbir // zaman takım yarıçapından (R) daha uzakta OLAMAZ (temas her zaman takım // yüzeyinde, eksene en fazla R uzaklıkta gerçekleşir). Bu yüzden ham ekstremler // ±R'ye kenetlenir. const f64 R = cap * 0.5; minX = std::clamp(minX, -R, R); maxX = std::clamp(maxX, -R, R); minY = std::clamp(minY, -R, R); maxY = std::clamp(maxY, -R, R); minZ = std::clamp(minZ, -R, R); maxZ = std::clamp(maxZ, -R, R); // TEK değer, eksen başına — seçim KAFANIN (takım ekseni e'nin) o eksendeki // YÖNÜNE göre yapılır: operatör XYZ sıfırlamasını takımın o an baktığı/eğildiği // tarafa göre yapar, bu yüzden e_i'nin işareti hangi ucun (min/max) geçerli // olduğunu belirler — keyfi bir "çapla karşılaştır" sezgiselliği YOK. Vec3d k; k.x = (e.x >= 0.0) ? maxX : minX; k.y = (e.y >= 0.0) ? maxY : minY; k.z = (e.z >= 0.0) ? maxZ : minZ; g_kacma = k; g_kacmaMin = {minX, minY, minZ}; g_kacmaMax = {maxX, maxY, maxZ}; } // Güncel takım konumu (TEK KAYNAK): kayarak modda [g_poz, g_poz+yön] segmentinde // interpolasyon, değilse node[g_poz]. Hem takım mesh'i hem gösterge XYZ bunu kullanır. Vec3d takimKonumGuncel() { const int son = (int)g_prog.noktalar.size() - 1; if (son < 0) return {}; const Vec3d& taban = g_prog.noktalar[(size_t)g_poz].p; if (!(g_cizgiUzerinde && g_yonu != 0 && son >= 1)) return taban; int nxt = g_poz + (g_yonu > 0 ? 1 : -1); if (nxt < 0) nxt = 0; if (nxt > son) nxt = son; const f64 f = std::clamp(g_zamanBirikim, 0.0, 1.0); return taban + (g_prog.noktalar[(size_t)nxt].p - taban) * f; } // Poz değişince: geçilen yol aralığını ayarla (O(1) — tampon dokunulmaz, web // setDrawRange eşleniği) + takımı taşı (hazirMeshGuncelle yerinde). Her-kare DEĞİL — // yalnız g_poz/g_pozMin değiştiğinde (playback/scrub). void simSahneGuncelle(Sahne& sahne, CamSistemi& cam) { if (!g_sahneGoster || !g_simGrup) return; // Tek/çoklu renk değişti → kovaları yeni renk düzeniyle yeniden kur (bir kez). if (g_tekRenk != g_tekRenkCache) { simSahneKur(sahne); if (!g_simGrup) return; } // "Çizgi önde" değişti → düğüm bayraklarını güncelle (O(1); tampon dokunulmaz). if (g_cizgiOnde != g_cizgiOndeCache) { simCizgiOndeUygula(sahne, g_cizgiOnde); g_cizgiOndeCache = g_cizgiOnde; } // Yol göster/gizle değişti → reveal'i bir kez yeniden uygula (aralık 0 ↔ hesaplanan). if (g_yolGoster != g_yolGosterCache) { g_yolGosterCache = g_yolGoster; g_sonCizilenPoz = -1; // pozDegisti'yi zorla } if (g_takimGoster != g_takimGosterCache) { g_takimGosterCache = g_takimGoster; g_sonCizilenPoz = -1; // takım düğümü bir kez kurulsun/silinsin (poz sabit olsa da) } if (g_tumYol != g_tumYolCache) { g_tumYolCache = g_tumYol; g_sonCizilenPoz = -1; // çizim aralığı tam ↔ reveal geçişi bir kez uygulansın } // Nokta göster/gizle: toggle'da kur/sil (O(1) kontrol). Yol gizliyse noktalar da gizli. const bool noktaIstek = g_noktaGoster && g_yolGoster; if (noktaIstek && !g_noktaDugum) simNoktaDugumKur(sahne); else if (!noktaIstek && g_noktaDugum) { sahne.sil(g_noktaDugum); g_noktaDugum = 0; } // "Çizgi üzerinde ilerleme" (web aktifTakimTasima): takım iki nokta arasında kayar → // takım HER KARE (frac değişir) yenilenir; reveal ise yalnız poz değişince. Kapalı = // eski gate (poz değişince tek yenileme). const int son = (int)g_prog.noktalar.size() - 1; const bool glide = g_cizgiUzerinde && g_yonu != 0 && son >= 1; const bool pozDegisti = !(g_poz == g_sonCizilenPoz && g_pozMin == g_sonCizilenMin); if (!pozDegisti && !glide) return; // ne poz değişti ne glide → iş yok if (pozDegisti) { g_sonCizilenPoz = g_poz; g_sonCizilenMin = g_pozMin; // Reveal (O(1) drawRange): tek çizgi → [2·pozMin, 2·poz); renkli → kova prefix. const int p = std::min(son, std::max(0, g_poz)); const int pMin = std::min(son, std::max(0, g_pozMin)); // Yol gizliyse çizim aralığı 0 (tampon dokunulmaz — churn'süz gizleme). // "Tüm yol" açıkken aralık pozdan BAĞIMSIZ: yolun tamamı (0..son). if (g_yolTekDugum) { const u32 bas = g_tumYol ? 0u : (u32)(2 * pMin); const u32 uc = g_tumYol ? (u32)(2 * son) : (u32)(2 * p); const u32 adet = (g_yolGoster && uc > bas) ? uc - bas : 0; sahne.kenarNesneCizAralik(g_yolTekDugum, bas, adet); } else if (!g_kovaOnek.empty()) { for (int b = 0; b < 4; ++b) { if (!g_yolGecilenDugum[b]) continue; const u32 kbas = g_tumYol ? 0u : g_kovaOnek[(size_t)pMin][b]; const u32 kuc = g_kovaOnek[(size_t)(g_tumYol ? son : p)][b]; const u32 adet = (g_yolGoster && kuc > kbas) ? kuc - kbas : 0; sahne.kenarNesneCizAralik(g_yolGecilenDugum[b], kbas, adet); } } } // Takım konumu: glide ise çizgi üzerinde interpolasyon; değilse poz noktasında. const SimNokta& n = g_prog.noktalar[(size_t)g_poz]; Vec3d tpos = n.p, tyon = n.takimYonu; if (glide) { int nxt = g_poz + (g_yonu > 0 ? 1 : -1); if (nxt < 0) nxt = 0; if (nxt > son) nxt = son; const f64 f = std::clamp(g_zamanBirikim, 0.0, 1.0); const SimNokta& nn = g_prog.noktalar[(size_t)nxt]; tpos = takimKonumGuncel(); // konum çizgi üzerinde kayar (tek kaynak) tyon = n.takimYonu + (nn.takimYonu - n.takimYonu) * f; // yön yumuşak geçiş if (tyon.uzunlukKare() < 1e-9) tyon = n.takimYonu; // Uç çizgi: revealed yol node[g_poz]'da biter → takım ucuna (tpos) kadar interpolasyonla // uzat. Düğüm kurulumda hazır (kenarNesneEkle DEĞİL) → yalnız güncelle + göster. if (g_ucDugum && g_yolGoster) { std::vector ucv = {{{(f32)n.p.x, (f32)n.p.y, (f32)n.p.z}, 0}, {{(f32)tpos.x, (f32)tpos.y, (f32)tpos.z}, 0}}; Aabbf us; us.genislet({(f32)n.p.x, (f32)n.p.y, (f32)n.p.z}); us.genislet({(f32)tpos.x, (f32)tpos.y, (f32)tpos.z}); sahne.kenarNesneGuncelle(g_ucDugum, ucv, us); sahne.kenarNesneCizAralik(g_ucDugum, 0, 2); // görünür (2 vertex = 1 çizgi) sahne.kenarNesneUstte(g_ucDugum, g_cizgiOnde); // ana yolla aynı derinlik davranışı } else if (g_ucDugum) { sahne.kenarNesneCizAralik(g_ucDugum, 0, 0); // yol gizli → uç çizgi de gizli } } else if (g_ucDugum) { sahne.kenarNesneCizAralik(g_ucDugum, 0, 0); // glide yok → uç çizgiyi gizle (churn'süz) } // Takım gizliyse düğümü kaldır (yeniden açılınca aşağıdaki yol yeniden kurar). if (!g_takimGoster) { if (g_takimDugum) { sahne.sil(g_takimDugum); g_takimDugum = 0; } return; } const cam::Takim* t = cam.takimBul((int)n.takimNo); MeshVeri tv; simTakimMesh(tpos, tyon, t, tv); if (!(g_takimDugum && sahne.hazirMeshGuncelle(g_takimDugum, tv))) { if (g_takimDugum) sahne.sil(g_takimDugum); g_takimDugum = sahne.hazirMeshEkle("Takım", g_simGrup, Vec3f{1.0f, 0.55f, 0.10f}, std::move(tv)); } } // ---- Renkli timeline + scrubber (ImGui DrawList) -------------------------- void timelineCiz() { const ImVec4 SOLUK = ui::tema().metinSoluk; const int adet = (int)g_prog.noktalar.size(); if (adet < 2) return; ImDrawList* dl = ImGui::GetWindowDrawList(); const ImVec2 p0 = ImGui::GetCursorScreenPos(); const f32 gen = ImGui::GetContentRegionAvail().x; const f32 yuk = 22.0f; if (gen < 8.0f) return; // geçersiz genişlik guard const ImU32 cerceve = ImGui::GetColorU32(ImGuiCol_Border); dl->AddRectFilled(p0, {p0.x + gen, p0.y + yuk}, IM_COL32(28, 30, 36, 255), 3.0f); const f32 pxPer = gen / (f32)(adet - 1); // Segmentler: her ardışık nokta çifti hareket türü rengiyle. for (int i = 1; i < adet; ++i) { const f32 x0 = p0.x + pxPer * (f32)(i - 1); const f32 x1 = p0.x + pxPer * (f32)i; dl->AddRectFilled({x0, p0.y + 3}, {x1, p0.y + yuk - 3}, hareketRenk(g_prog.noktalar[(size_t)i].tur), 0.0f); } // Oynatma aralığı dışını karart (pick/pozMin-pozMax; web pozMin/pozMax görseli). const int maxE = pozMaxEtkin(); if (g_pozMin > 0) dl->AddRectFilled({p0.x, p0.y + 3}, {p0.x + pxPer * (f32)g_pozMin, p0.y + yuk - 3}, IM_COL32(8, 9, 12, 190)); if (maxE < adet - 1) dl->AddRectFilled({p0.x + pxPer * (f32)maxE, p0.y + 3}, {p0.x + gen, p0.y + yuk - 3}, IM_COL32(8, 9, 12, 190)); // Operasyon sınır çizgileri. for (const OpSinir& s : g_prog.sinirlar) { if (s.baslangicIdx <= 0 || s.baslangicIdx >= adet) continue; const f32 x = p0.x + pxPer * (f32)s.baslangicIdx; dl->AddLine({x, p0.y}, {x, p0.y + yuk}, IM_COL32(255, 255, 255, 160), 1.5f); } // İlerleme imleci. const f32 xImlec = p0.x + pxPer * (f32)g_poz; dl->AddLine({xImlec, p0.y - 2}, {xImlec, p0.y + yuk + 2}, IM_COL32(255, 255, 255, 255), 2.0f); dl->AddCircleFilled({xImlec, p0.y - 2}, 3.0f, IM_COL32(255, 255, 255, 255)); dl->AddRect(p0, {p0.x + gen, p0.y + yuk}, cerceve, 3.0f); // Tıkla → o poza atla (segment seçimi). ImGui::InvisibleButton("##timeline", {gen, yuk}); if (ImGui::IsItemActive() || ImGui::IsItemClicked()) { const f32 rel = (ImGui::GetIO().MousePos.x - p0.x) / gen; int hedef = (int)(rel * (f32)(adet - 1) + 0.5f); if (hedef < 0) hedef = 0; if (hedef > adet - 1) hedef = adet - 1; g_poz = hedef; g_yonu = 0; g_zamanBirikim = 0; } // Renk açıklaması. auto lejant = [&](HareketTuru t) { ImVec2 c = ImGui::GetCursorScreenPos(); const f32 s = ImGui::GetFontSize() * 0.7f; ImGui::GetWindowDrawList()->AddRectFilled(c, {c.x + s, c.y + s}, hareketRenk(t), 2.0f); ImGui::Dummy({s + 4, s}); ImGui::SameLine(); ImGui::TextColored(SOLUK, "%s", hareketAdi(t)); }; lejant(HareketTuru::Isleme); ImGui::SameLine(); lejant(HareketTuru::Yaklasma); ImGui::SameLine(); lejant(HareketTuru::Uzaklasma);ImGui::SameLine(); lejant(HareketTuru::Bosta); } // ---- ImDrawList ikonları (emoji yerine vektörel — transport + reset/pick) -------- // tur: 0 reset(↺) · 1 pick(crosshair) · 2 rewind(◀◀) · 3 back(◀) · 4 pause(‖) // 5 play(▶) · 6 forward(▶▶). c: merkez, s: yarı-boyut (px). void simgeCiz(ImDrawList* dl, ImVec2 c, float s, int tur, ImU32 col) { const float th = std::max(1.4f, s * 0.32f); auto triSag = [&](float ox) { // sağ üçgen (play), ox merkez kaydırma dl->AddTriangleFilled({c.x + ox - s * 0.55f, c.y - s}, {c.x + ox - s * 0.55f, c.y + s}, {c.x + ox + s * 0.60f, c.y}, col); }; auto triSol = [&](float ox) { // sol üçgen (back) dl->AddTriangleFilled({c.x + ox + s * 0.55f, c.y - s}, {c.x + ox + s * 0.55f, c.y + s}, {c.x + ox - s * 0.60f, c.y}, col); }; switch (tur) { case 2: triSol(-s * 0.5f); triSol(s * 0.65f); break; // rewind case 3: triSol(0.0f); break; // back case 4: // pause dl->AddRectFilled({c.x - s * 0.55f, c.y - s}, {c.x - s * 0.15f, c.y + s}, col, th * 0.35f); dl->AddRectFilled({c.x + s * 0.15f, c.y - s}, {c.x + s * 0.55f, c.y + s}, col, th * 0.35f); break; case 5: triSag(0.0f); break; // play case 6: triSag(-s * 0.65f); triSag(s * 0.5f); break; // forward case 0: { // reset (dairesel ok) dl->PathArcTo(c, s, IM_PI * 0.35f, IM_PI * 1.95f, 16); dl->PathStroke(col, 0, th * 0.62f); const float a = IM_PI * 0.35f; const ImVec2 u{c.x + std::cos(a) * s, c.y + std::sin(a) * s}; dl->AddTriangleFilled({u.x - th * 1.1f, u.y - th * 1.4f}, {u.x + th * 1.5f, u.y - th * 0.1f}, {u.x - th * 0.5f, u.y + th * 1.4f}, col); break; } case 1: // pick (crosshair) dl->AddCircle(c, s * 0.82f, col, 0, th * 0.55f); dl->AddLine({c.x - s, c.y}, {c.x - s * 0.4f, c.y}, col, th * 0.55f); dl->AddLine({c.x + s * 0.4f, c.y}, {c.x + s, c.y}, col, th * 0.55f); dl->AddLine({c.x, c.y - s}, {c.x, c.y - s * 0.4f}, col, th * 0.55f); dl->AddLine({c.x, c.y + s * 0.4f}, {c.x, c.y + s}, col, th * 0.55f); dl->AddCircleFilled(c, th * 0.65f, col); break; case 7: { // toolpath ilerleme (çizgi + ilerleyen nokta) — web "çizgi önde" ikonu dl->AddLine({c.x - s, c.y}, {c.x + s, c.y}, col, th * 0.5f); const float mx = c.x + s * 0.25f; dl->AddLine({c.x - s, c.y}, {mx, c.y}, col, th * 0.9f); // geçilen kısım kalın dl->AddCircleFilled({mx, c.y}, th * 0.95f, col); // ilerleme markörü break; } } } // Kare simge butonu (verilen `boy` = kenar; edit yüksekliğiyle hizalanır). İkon // ImDrawList ile çizilir (glyph/emoji yok). Tık → true. bool simgeButon(const char* kimlik, int tur, const char* ipucu, float boy) { const ImVec2 bp = ImGui::GetCursorScreenPos(); const bool t = ImGui::Button(kimlik, {boy, boy}); simgeCiz(ImGui::GetWindowDrawList(), {bp.x + boy * 0.5f, bp.y + boy * 0.5f}, boy * 0.30f, tur, ImGui::GetColorU32(ImGuiCol_Text)); if (ipucu && ImGui::IsItemHovered()) ImGui::SetTooltip("%s", ipucu); return t; } // ---- Göstergeler: sağ kolon kompakt bilgi (web fiko-similasyon sağ blok eşleniği) --- void gostergeler(Sahne& sahne, CamSistemi& cam) { const ImVec4 SOLUK = ui::tema().metinSoluk; const int adet = (int)g_prog.noktalar.size(); if (adet == 0) { ImGui::TextColored(SOLUK, "Nokta yok"); return; } const SimNokta& n = g_prog.noktalar[(size_t)g_poz]; auto ortali = [](const char* s) { const float w = ImGui::GetContentRegionAvail().x; const float tw = ImGui::CalcTextSize(s).x; if (tw < w) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (w - tw) * 0.5f); ImGui::TextUnformatted(s); }; char buf[96]; // op | hareket | takım (web "Roughing.2 | İşleme | 30 R15") const char* opAd = "—"; for (const OpSinir& s : g_prog.sinirlar) if (n.opId == s.opId) opAd = s.ad.c_str(); const cam::Takim* t = cam.takimBul((int)n.takimNo); const float huSol = ImGui::GetCursorPosX(); // hücre sol (table sütunu) const float huGen = ImGui::GetContentRegionAvail().x; // hücre genişliği if (t) ImGui::Text("%s | %s | %.0f R%.0f", opAd, hareketAdi(n.tur), t->cap, t->koseYaricap); else ImGui::Text("%s | %s | T%u", opAd, hareketAdi(n.tur), n.takimNo); // Web tarzı: op adının yanında SAĞA DAYALI kütük hızlı butonları (hücre sağına). { const float bw = ImGui::GetFrameHeight(); const float sp = ImGui::GetStyle().ItemSpacing.x; ImGui::SameLine(); ImGui::SetCursorPosX(huSol + huGen - bw * 2 - sp); // Kütük simülasyon modu aç/kapa (ana buton ile aynı toggle). if (ImGui::Button("🧊##kutukKur", {bw, bw})) { if (g_kutuk.kuruldu) kutukKapat(sahne); else kutukKur(cam); } if (ImGui::IsItemHovered()) ImGui::SetTooltip(g_kutuk.kuruldu ? "Kütük simülasyon modu AÇIK — tıkla: kaldır" : "Kütük simülasyon modu — tıkla: kütük kur"); ImGui::SameLine(0, sp); const bool opVar = cam.aktifParcaOp > 0; if (!opVar) ImGui::BeginDisabled(); if (ImGui::Button("🖱##kutukSec", {bw, bw}) && opVar) camParcaOpPencereAc((u32)cam.aktifParcaOp); // Stok sekmesi: çizimden gövde seç if (!opVar) ImGui::EndDisabled(); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Çizimden kütük seç — Part Operation Stok editörünü aç " "(tip/boyut veya sahnede kaynak gövdeye tıkla)"); } // Üst / Alt açı (ortalı) const Vec3d yon = n.takimYonu; const f64 ust = std::atan2(std::hypot(yon.x, yon.y), yon.z) * 180.0 / 3.14159265358979; const f64 alt = std::atan2(yon.y, yon.x) * 180.0 / 3.14159265358979; std::snprintf(buf, sizeof buf, "Üst Açı=%.*f Alt Açı=%.*f", birim::hane(birim::Birim::Aci), ust, birim::hane(birim::Birim::Aci), alt); ortali(buf); // Kaçma (runout) — analitik; takım/yön değişince yenilenir (web ConvexHull eşleniği). ortali("Kaçma Değerleri"); if (g_kacmaTakim != n.takimNo || (g_kacmaYon - n.takimYonu).uzunlukKare() > 1e-12) { kacmaHesapla(cam.takimBul((int)n.takimNo), n.takimYonu); g_kacmaTakim = n.takimNo; g_kacmaYon = n.takimYonu; } std::snprintf(buf, sizeof buf, "X:%.*f Y:%.*f Z:%.*f", birim::hane(birim::Birim::Uzunluk), g_kacma.x, birim::hane(birim::Birim::Uzunluk), g_kacma.y, birim::hane(birim::Birim::Uzunluk), g_kacma.z); ortali(buf); // İşleme ekseni (web "Fikoperasyon.1.İşleme Ekseni") const char* eksenAd = (std::abs(yon.z) > 0.99) ? "Z" : (std::abs(yon.x) > 0.99) ? "X" : (std::abs(yon.y) > 0.99) ? "Y" : "Serbest"; std::snprintf(buf, sizeof buf, "%s · İşleme Ekseni", eksenAd); ortali(buf); } // ---- Oynatma ilerletme (io.DeltaTime tabanlı) ----------------------------- void oynatIlerlet() { if (g_yonu == 0) return; const int adet = (int)g_prog.noktalar.size(); if (adet < 2) return; const f32 dt = ImGui::GetIO().DeltaTime; const int maxE = pozMaxEtkin(); if (g_cizgiUzerinde) { // KAYARAK mod — MESAFE tabanlı (mm/sn): kare başına (simHizi·reel/100·dt) mm kat // edilir; frac segment UZUNLUĞUNA göre ilerler → yoğun/seyrek nokta fark etmez, // takım sabit hızda kayar. g_zamanBirikim = [g_poz, g_poz+yön] segmentindeki frac. f64 kalanMm = (f64)g_simHizi * ((f64)g_reelIlerleme / 100.0) * (f64)dt; int guard = adet + 4; // dejenere segment zinciri sonsuz döngü guard while (kalanMm > 0.0 && guard-- > 0) { if (g_yonu > 0 && g_poz >= maxE) { g_poz = maxE; g_yonu = 0; g_zamanBirikim = 0; break; } if (g_yonu < 0 && g_poz <= g_pozMin) { g_poz = g_pozMin; g_yonu = 0; g_zamanBirikim = 0; break; } const int nxt = g_poz + g_yonu; if (nxt < 0 || nxt >= adet) { g_yonu = 0; g_zamanBirikim = 0; break; } const f64 segLen = g_prog.noktalar[(size_t)g_poz].p.mesafe(g_prog.noktalar[(size_t)nxt].p); if (!(segLen > 1e-9)) { g_poz = nxt; g_zamanBirikim = 0; continue; } // dejenere → geç const f64 segKalan = segLen * (1.0 - g_zamanBirikim); // bu segmentte kalan mesafe if (kalanMm >= segKalan) { kalanMm -= segKalan; g_poz = nxt; g_zamanBirikim = 0; } else { g_zamanBirikim += kalanMm / segLen; kalanMm = 0.0; } // segment içinde kal } } else { // NOKTA modu (web): noktadan noktaya, nokta/sn. Interpolasyon yok. g_zamanBirikim += (f64)dt * (f64)g_simHizi * ((f64)g_reelIlerleme / 100.0); while (g_zamanBirikim >= 1.0) { g_zamanBirikim -= 1.0; g_poz += g_yonu; if (g_poz <= g_pozMin) { g_poz = g_pozMin; g_yonu = 0; break; } if (g_poz >= maxE) { g_poz = maxE; g_yonu = 0; break; } } } } // Simülasyon Motoru — AYRI pencere (web referansı: sol "Simülasyon Motoru" paneli). // Kütük motoru seçimi (buton ızgarası) + ayarlar + oyma kontrolleri + istatistik. // Ana sim penceresindeki buton g_motorPencereAcik'i açar. void simMotorPenceresiCiz(Sahne& sahne, cam::CamSistemi& cam) { if (!g_motorPencereAcik) return; const ImVec4 CYAN = ui::tema().vurgu; const ImVec4 HATA_RENK = ui::tema().hata; const ImVec4 SOLUK = ui::tema().metinSoluk; ui::Pencere p{"🧱 Simülasyon Motoru###sim_motor", &g_motorPencereAcik, {.boyut = {300, 0}, .pinDurum = &g_motorPencerePin}}; if (!p) return; // Motor seçimi: buton ızgarası (web tarzı). struct KipBtn { int kip; const char* et; }; const KipBtn kbtn[] = {{3, "TriDexel Pro\n3D · Keskin"}, {2, "SDF+MC\n3D · Pürüzsüz"}, {0, "Triple-Dexel\n2.5D · Hız"}, {1, "Z-Buffer\n6-Dir · Enfazlı"}}; const float gw = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) / 2.0f; for (int i = 0; i < 4; ++i) { if (i % 2) ImGui::SameLine(); const bool sc = g_kutuk.kip == kbtn[i].kip; if (sc) ImGui::PushStyleColor(ImGuiCol_Button, CYAN); if (ImGui::Button(kbtn[i].et, {gw, 36})) { g_kutuk.kip = kbtn[i].kip; g_kutuk.kuruldu = false; } if (sc) ImGui::PopStyleColor(); } ImGui::SetItemTooltip("TriDexel Pro: profesyonel (Vericut sınıfı) analitik tri-dexel — " "yüzey noktaları grid'e yuvarlanmaz, keskin kenar korunur"); // GPU varyant yalnız Dexel3/SDF için var; ZBuffer6/TriDexel CPU-only. ÖLÇÜLDÜ: GPU // carve yansımıyor (readback/hacim) → deneysel, varsayılan KAPALI + uyarı. if (g_kutuk.kip == 0 || g_kutuk.kip == 2) { if (ImGui::Checkbox("GPU (deneysel — oymayabilir)", &g_kutuk.gpu)) g_kutuk.kuruldu = false; ImGui::SetItemTooltip("ÖLÇÜLDÜ: GPU kütük oyması eksik (readback/hacim). " "Oymuyorsa KAPAT — CPU tam oyar (kanıtlı)."); if (g_kutuk.gpu) ImGui::TextColored(HATA_RENK, "⚠ GPU deneysel: oymazsa kapat (CPU oyar)"); } else { g_kutuk.gpu = false; ImGui::TextColored(SOLUK, g_kutuk.kip == 3 ? "TriDexel Pro: CPU analitik (keskin)" : "ZBuffer6 yalnız CPU"); } ImGui::SetNextItemWidth(-1); if (ImGui::SliderFloat("##coz", &g_kutuk.cozunurluk, 0.1f, 4.0f, "Çözünürlük: %.2f mm")) g_kutuk.kuruldu = false; ImGui::Checkbox("Takımı stok üstüne uzat (derin dalışta tavan bırakma)", &g_kutukTakimUzat); ImGui::SetItemTooltip("Kesici boyu dalış derinliğinden kısaysa tip'in üstünde kesilmemiş " "malzeme (2-yüzey/'iterek' görünüm) kalır. Açık: takım katısı stok " "üstüne kadar keser. Kapalı: gerçek kesici boyu."); ImGui::Separator(); // Kütük Simülasyon Modu = AÇ/KAPA toggle: ilk tık kütüğü kurar, ikinci tık siler. if (ImGui::Button(g_kutuk.kuruldu ? "⬒ Kütük Simülasyon Modu ✓" : "⬒ Kütük Simülasyon Modu", {-1, 0})) { if (g_kutuk.kuruldu) kutukKapat(sahne); else kutukKur(cam); } ImGui::SetItemTooltip(g_kutuk.kuruldu ? "Kütük simülasyonu AÇIK — tıkla: kütüğü kaldır" : "Tıkla: aktif Part Operation stoğundan (yoksa yol bbox + pay) " "kütük kur ve talaş kaldırmayı simüle et"); if (ImGui::Checkbox("⚡ Async carve (gerçek-zamanlı)", &g_kutukAsyncTercih)) { if (!g_kutukAsyncTercih && g_kutukAsync.calisiyorMu()) { g_kutukAsync.durdur(); // kapatıldı → worker'ı durdur (yeniden Kür gerekir) g_kutuk.kuruldu = false; g_kutuk.log = "Async kapatıldı — senkron için 'Kütük Kur' ile yeniden kur"; } } ImGui::SetItemTooltip("Talaş kaldırma worker thread'inde koşar → oynatma bloklanmaz. " "GPU motorunda otomatik kapalı."); if (ImGui::Checkbox("◇ Keskin kenar (Dual Contouring)", &g_kutukKeskin)) { g_kutukAsync.keskinAta(g_kutukKeskin); // async worker'a if (g_kutuk.motor) { g_kutuk.motor->keskinMeshAta(g_kutukKeskin); g_kutuk.meshBayat = true; } } ImGui::SetItemTooltip("Keskin köşe/kenarı korur (MC yuvarlar). SDF+MC kipinde. Analitik " "keskin normaller için AÇTIKTAN SONRA 'Kütük Kur'a tekrar bas. " "Cache'siz tam grid → daha yavaş güncelleme (kalite görünümü)."); // Async'te motor worker'a devredildi (g_kutuk.motor null) ama grid çalışıyor → hazır say. const bool hazir = g_kutuk.kuruldu && (g_kutuk.motor || g_kutukAsync.calisiyorMu()); if (!hazir) ImGui::BeginDisabled(); ImGui::Checkbox("▶ Sim ile Oyma (replay senkron)", &g_kutuk.otoOy); ImGui::SetItemTooltip("Oynatma ilerledikçe geçilen hamleler kütükten malzeme kaldırır"); ImGui::SameLine(); if (g_kutuk.hedefIstek >= 0) { if (ImGui::Button("⏹ Oymayı Durdur")) g_kutuk.hedefIstek = -1; } else if (ImGui::Button("⏩ Sona Kadar Oy")) { g_kutuk.hedefIstek = (int)g_prog.noktalar.size() - 1; // kare-kare bütçeyle (UI donmaz) } ImGui::SetItemTooltip("Tüm programı kare-kare zaman bütçesiyle işle (UI akıcı kalır)"); ImGui::SetNextItemWidth(140); ImGui::SliderFloat("Bütçe ms/kare", &g_kutukBatchMs, 1.0f, 33.0f, "%.0f ms"); ImGui::SetItemTooltip("Kare başına oyma süresi — büyük = hızlı biter, küçük = akıcı UI"); ImGui::SetNextItemWidth(140); ImGui::SliderFloat("Mesh yenile ms", &g_kutukMeshMs, 16.0f, 1000.0f, "%.0f ms"); ImGui::SetItemTooltip("Kütük görselinin çıkarılma aralığı (eski: her kare → kasıyordu)"); ImGui::Text("Kaldırılan hacim: %.1f mm³ (%.2f cm³)", g_kutuk.hacimMm3, g_kutuk.hacimMm3 / 1000.0); ImGui::Text("İşlenen: %d / %d hamle", g_kutuk.oyulanPoz, (int)g_prog.noktalar.size() - 1); ImGui::TextColored(g_kutuk.sonMeshVert > 3000000 ? HATA_RENK : SOLUK, "Kütük mesh: %.2f M vertex (%.0f MB)", g_kutuk.sonMeshVert / 1e6, g_kutuk.sonMeshVert * 48.0 / 1048576.0); // RADIUS TEŞHİSİ: aktif mesh kipi + GERÇEK hücre boyu. Keskinlik hücre boyuyla // sınırlı — hücre > ~2mm ise keskin köşeler ~hücre kadar yuvarlanır (DC bile). { const char* kip = g_kutuk.kip == 3 ? "TriDexel Pro (analitik keskin)" : g_kutuk.kip == 2 ? (g_kutukKeskin ? "Dual Contouring (keskin)" : "Marching Cubes") : "kip mesh'i (grid)"; // TriDexel'de yüzey noktaları ANALİTİK (hücreden bağımsız keskin) → kaba uyarısı yok. const bool kabaHucre = g_kutuk.kip != 3 && g_kutuk.gercekCoz > 2.0f; ImGui::TextColored(kabaHucre ? HATA_RENK : CYAN, "Mesh: %s · Hücre: %.2f mm%s", kip, g_kutuk.gercekCoz, kabaHucre ? " ⚠ kaba → köşe yuvarlanır" : ""); if (kabaHucre) ImGui::TextColored(SOLUK, " Keskin için: TriDexel Pro kipini seç (analitik) veya " "'Kütük maks hücre'yi artır."); } // ASYNC ÖLÇÜM (gerçek-zaman darboğazı teşhisi — TAHMİN DEĞİL): worker'ın son 1 sn'de // carve'e vs mesh çıkarmaya harcadığı ms + hamle/sn + backlog. carve≈1000ms → carve-bound // (grid kabalaştır/GPU); mesh yüksek → mesh-bound (paralel MC/aralık); backlog büyürse yetişmiyor. if (g_kutukAsync.calisiyorMu()) { const f64 cMs = g_kutukAsync.carveMsSn(), mMs = g_kutukAsync.meshMsSn(); const int bl = g_kutukAsync.backlog(); ImGui::TextColored(bl > 2000 ? HATA_RENK : CYAN, "⚡ Async: carve %.0f ms/sn · mesh %.0f ms/sn · %.0f hamle/sn · backlog %d", cMs, mMs, g_kutukAsync.hamleSn(), bl); } if (auto* dx = dynamic_cast(g_kutuk.motor.get())) { ImGui::TextColored(dx->birlestirmeSayisi() ? HATA_RENK : SOLUK, "Span birleştirme: %llu Süpürme cap uyarısı: %s", (unsigned long long)dx->birlestirmeSayisi(), dx->supurmeCapUyarisi() ? "VAR" : "yok"); } ImGui::Separator(); if (ImGui::Button("👁 Mesh Göster/Yenile", {-100, 0})) kutukMeshYenile(sahne); ImGui::SetItemTooltip("Güncel kütüğü sahnede gri mesh olarak göster (istek üzerine)"); ImGui::SameLine(); if (ImGui::Button("↺ Sıfırla", {-1, 0})) kutukSifirla(sahne); if (ImGui::Button("⬇ OBJ dışa aktar", {-1, 0})) { const std::string yol = "D:/__sistem__/Downloads/Test/kutuk_disari.obj"; g_kutuk.log = kutukObjYaz(yol) ? ("OBJ yazıldı: " + yol) : "OBJ yazılamadı (kütük boş/kurulu değil)"; } ImGui::SetItemTooltip("Oyulan kütüğü .obj (v+vn+f) olarak yaz — normal/geometri teşhisi"); if (!hazir) ImGui::EndDisabled(); if (!g_kutuk.log.empty()) ImGui::TextColored(g_kutuk.log.find("hata") != std::string::npos || g_kutuk.log.find("değil") != std::string::npos ? HATA_RENK : SOLUK, "%s", g_kutuk.log.c_str()); if (!hazir) ImGui::TextColored(SOLUK, "Önce 'Kütük Kur' ile grid'i oluştur."); } } // namespace void camSimPencereAc(u32 opKimlik) { g_simAcik = true; g_simOpKimlik = opKimlik; // Program burada kurulmaz — çizimde CamSistemi erişimiyle kurulur (aşağıda bayrak). g_prog.noktalar.clear(); // yeniden kurulmayı tetikle g_poz = 0; g_yonu = 0; g_zamanBirikim = 0; g_pozMin = 0; g_pozMax = -1; g_pickHedef = -1; } void camSimPenceresiCiz(UygulamaBaglam& b, VulkanBaglam& vb) { Sahne& sahne = b.sahne; cam::CamSistemi& cam = b.camSis; const ImVec4 CYAN = ui::tema().vurgu; const ImVec4 HATA_RENK = ui::tema().hata; const ImVec4 SOLUK = ui::tema().metinSoluk; g_kutukVb = &vb; // GPU kütük varyantı için if (!g_simAcik) { perfGosterge().simAktif = false; return; } // Program kurulmamışsa (yeni açıldı) kur. Kütük ilerlemesi de sıfırlanır // (yol değişti → eski oyma geçersiz; mesh düğümü kaldırılır). if (g_prog.noktalar.empty()) { const auto t0 = std::chrono::steady_clock::now(); g_prog = programKur(cam, g_simOpKimlik); g_poz = 0; g_yonu = 0; g_zamanBirikim = 0; g_kutukAsync.durdur(); // yeni yol → eski async carve geçersiz g_kutuk.kuruldu = false; g_kutuk.oyulanPoz = 0; g_kutuk.sonNoktaVar = false; if (g_kutuk.meshDugum) { sahne.sil(g_kutuk.meshDugum); g_kutuk.meshDugum = 0; } simSahneKur(sahne); // yolu 3B sahneye çiz (web similasyonCizgisi eşleniği) const f64 kurMs = std::chrono::duration( std::chrono::steady_clock::now() - t0).count(); perfGosterge().simKurMs = kurMs; perfGosterge().simNoktaSayi = (u32)g_prog.noktalar.size(); // ÖLÇÜM (tahmin değil): kaç nokta, ne kadar sürdü, hangi kip. gunluk::bilgi("[sim] kur: {} nokta, {:.1f} ms, kip={}", g_prog.noktalar.size(), kurMs, g_yolTekDugum ? "tek-cizgi" : "renkli-kova"); // Hareket türü histogramı: KESME (Isleme+Yaklasma → oyar) vs rapid (oymaz). // hacim=0 sebebi buradan görünür: oyacak kesme hamlesi var mı, kesme aralığı ne? u32 hIsleme = 0, hYaklas = 0, hUzak = 0, hBosta = 0; Aabbf ks; for (const SimNokta& n : g_prog.noktalar) { switch (n.tur) { case HareketTuru::Isleme: ++hIsleme; break; case HareketTuru::Yaklasma: ++hYaklas; break; case HareketTuru::Uzaklasma: ++hUzak; break; default: ++hBosta; break; } if (n.tur == HareketTuru::Isleme || n.tur == HareketTuru::Yaklasma) ks.genislet({(f32)n.p.x, (f32)n.p.y, (f32)n.p.z}); } const Vec3f ksB = ks.gecerliMi() ? ks.boyut() : Vec3f{0, 0, 0}; gunluk::bilgi("[sim] hamle türü: kesme(Isleme={} Yaklaşma={}) rapid(Uzak={} Boşta={}); " "kesme bölgesi={:.1f}×{:.1f}×{:.1f}mm", hIsleme, hYaklas, hUzak, hBosta, ksB.x, ksB.y, ksB.z); } perfGosterge().simAktif = true; pozKilitle(); // Ayar/checkbox ile "Sim sahne görseli" açılıp kapanınca düğümleri uzlaştır (O(1)). if (!g_sahneGoster && g_simGrup) simSahneTemizle(sahne); else if (g_sahneGoster && !g_simGrup && g_prog.noktalar.size() >= 2) simSahneKur(sahne); using namespace fiko::ui; using u32 = fiko::u32; // ui::u32(ImVec4) ile tip cakismasini onler bool acik = true; bool govde = false; { // Görünüm = FikoUi WebCam skin'i (teal cam zemin + cyan kenar + grid + pill // slider); Pencere içeride kurar. Grid boyu: Ayarlar → Pencere (merkezî). ui::Pencere p{"Similasyon###cam_sim", &acik, {.boyut = {360, 0}, .pinDurum = &g_simPin, .skin = ui::PencereSkin::WebCam}}; govde = (bool)p; if (p) { // Sahne görseli KAPALIYKEN yol/takım 3B sahnede hiç çizilmez (simSahneKur erken // çıkar) — kullanıcı "yolu göremiyorum" diye takılıyordu. Sebebi görünür yap + tek // tıkla düzelt. (Ayar kalıcı: cam.sim_sahne_gorsel) if (!g_sahneGoster) { ImGui::TextColored(HATA_RENK, "⚠ Sahne görseli KAPALI — yol/takım 3B sahnede çizilmiyor"); if (ImGui::Button("👁 Sahne görselini aç", {-1, 0})) g_sahneGoster = true; } if (g_prog.demo) ImGui::TextColored(HATA_RENK, "Hesaplanmış yol yok — DEMO yol gösteriliyor"); else ImGui::TextColored(SOLUK, "%d operasyon · %d nokta", (int)g_prog.sinirlar.size(), (int)g_prog.noktalar.size()); const int son = (int)g_prog.noktalar.size() - 1; // ---- Oynatma: web "ui buttons" grubu (açık pill + ImDrawList ikon + ayraç) ---- { const float h = 22.0f; const float w = ImGui::GetContentRegionAvail().x; const ImVec2 p0 = ImGui::GetCursorScreenPos(); const float r = h * 0.5f; const float bw = w / 5.0f; ImDrawList* dl = ImGui::GetWindowDrawList(); dl->AddRectFilled(p0, {p0.x + w, p0.y + h}, IM_COL32(233, 240, 243, 240), r); dl->AddRect(p0, {p0.x + w, p0.y + h}, IM_COL32(255, 255, 255, 70), r, 0, 1.0f); for (int i = 1; i < 5; ++i) // iç ayraç çizgileri dl->AddLine({p0.x + bw * (float)i, p0.y + 5.0f}, {p0.x + bw * (float)i, p0.y + h - 5.0f}, IM_COL32(120, 140, 150, 90), 1.0f); ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(0, 0, 0, 0)); // saydam (pill görünsün) ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(0, 90, 105, 70)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, IM_COL32(0, 150, 175, 110)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, ImGui::GetStyle().ItemSpacing.y)); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, r); const ImVec2 bs{bw, h}; const int simge[5] = {2, 3, 4, 5, 6}; // rewind · back · pause · play · forward const char* pid[5] = {"##pb0", "##pb1", "##pb2", "##pb3", "##pb4"}; for (int i = 0; i < 5; ++i) { if (i) ImGui::SameLine(); const ImVec2 bp = ImGui::GetCursorScreenPos(); const bool tik = ImGui::Button(pid[i], bs); // saydam; ikon üstüne çizilir simgeCiz(dl, {bp.x + bw * 0.5f, bp.y + h * 0.5f}, h * 0.26f, simge[i], IM_COL32(40, 54, 62, 255)); if (tik) switch (i) { case 0: g_poz = g_pozMin; g_yonu = 0; g_zamanBirikim = 0; break; case 1: g_yonu = -1; g_zamanBirikim = 0; break; case 2: g_yonu = 0; break; case 3: g_yonu = 1; g_zamanBirikim = 0; break; case 4: g_poz = pozMaxEtkin(); g_yonu = 0; g_zamanBirikim = 0; break; } } ImGui::PopStyleVar(2); ImGui::PopStyleColor(3); } // ---- Renkli timeline (web renkli çubuk) ---- ImGui::Spacing(); timelineCiz(); // ---- Konum slider (web mavi range) ---- if (son >= 1) { ImGui::SetNextItemWidth(-1); if (ImGui::SliderInt("##poz", &g_poz, g_pozMin, pozMaxEtkin(), "%d")) { g_yonu = 0; g_zamanBirikim = 0; } } ui::neonAyrac(); // ---- 2 kolon: sol aralık girişleri · sağ göstergeler (web layout) ---- if (ImGui::BeginTable("##simkol", 2, ImGuiTableFlags_NoClip)) { ImGui::TableSetupColumn("##sol", ImGuiTableColumnFlags_WidthFixed, 120.0f); ImGui::TableSetupColumn("##sag", ImGuiTableColumnFlags_WidthStretch); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); // Başlangıç / Şimdiki / Bitiş: [sıfırla][sayı][sahne-pick] (web üç satır). auto araligSatir = [&](const char* etiket, int* deger, int dmin, int dmax, int pickHedef, int sifir) { ImGui::TextUnformatted(etiket); ImGui::PushID(etiket); const float fh = ImGui::GetFrameHeight(); // sıfırla/pick edit ile eşit yükseklik const float editW = std::max(30.0f, ImGui::GetContentRegionAvail().x - (fh + 2.0f) * 2.0f); if (simgeButon("##sifir", 0, "Sıfırla", fh)) { *deger = sifir; g_yonu = 0; g_zamanBirikim = 0; } ImGui::SameLine(0.0f, 2.0f); ImGui::SetNextItemWidth(editW); // edit'i kolona yay (sağ boşluğu doldur) if (ImGui::DragInt("##v", deger, 1.0f, dmin, dmax)) { g_yonu = 0; g_zamanBirikim = 0; } ImGui::SameLine(0.0f, 2.0f); if (simgeButon("##pick", 1, "Sahnede nokta seç", fh)) g_pickHedef = pickHedef; ImGui::PopID(); }; araligSatir("Başlangıç :", &g_pozMin, 0, son, 0, 0); araligSatir("Şimdiki :", &g_poz, g_pozMin, pozMaxEtkin(), 1, g_pozMin); { int pmv = pozMaxEtkin(); araligSatir("Bitiş :", &pmv, g_pozMin, son, 2, son); g_pozMax = (pmv >= son) ? -1 : pmv; // son → -1 (tam aralık); pozKilitle sonra netler } ImGui::TableSetColumnIndex(1); const float fhz = ImGui::GetFrameHeight(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - fhz - 4.0f); ImGui::SliderInt("##simHizi", &g_simHizi, 1, 1000, g_cizgiUzerinde ? "Hız: %d mm/sn" : "Hız: %d nk/sn"); ImGui::SameLine(0.0f, 2.0f); // web: slider sağında "çizgi üzerinde ilerleme" butonu if (g_cizgiUzerinde) ImGui::PushStyleColor(ImGuiCol_Button, CYAN); if (simgeButon("##cizgiUzeri", 7, "Takım çizgi üzerinde kayarak ilerlesin (noktadan noktaya zıplama yerine)", fhz)) g_cizgiUzerinde = !g_cizgiUzerinde; if (g_cizgiUzerinde) ImGui::PopStyleColor(); gostergeler(sahne, cam); ImGui::EndTable(); } ui::neonAyrac(); // ---- Alt bilgi (web tam genişlik) ---- if (son >= 0) { const SimNokta& n = g_prog.noktalar[(size_t)g_poz]; const f64 rp = g_reelIlerleme / 100.0; ImGui::Text("İlerleme : %.0f mm_dk | %s", n.hiz * rp, hareketAdi(n.tur)); ImGui::SameLine(); { char db[48]; std::snprintf(db, sizeof db, "Devir: %.0f rpm", n.devir); const float off = ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(db).x; if (off > 0.0f) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + off); ImGui::TextUnformatted(db); } const Vec3d kp = takimKonumGuncel(); // interpolasyonlu güncel takım konumu (glide) ImGui::Text("X=%.6f Y=%.6f Z=%.6f", kp.x, kp.y, kp.z); const f64 pay = rp > 1e-9 ? rp : 1.0; // sıfır bölme guard ImGui::Text("Geçen : %.4f dk Toplam : %.4f dk", n.guncelSure / pay, g_prog.toplamSure / pay); } // ---- Reel yüzdelik ilerleme (web %90 + slider) ---- ImGui::Text("Reel Yüzdelik İlerleme : %%%d", g_reelIlerleme); ImGui::SetNextItemWidth(-1); ImGui::SliderInt("##reel", &g_reelIlerleme, 1, 200, ""); ui::neonAyrac(); // ---- Alt bar: Araçlar (sol) · Kapat (sağ) ---- if (ImGui::Button("Araçlar ▾")) ImGui::OpenPopup("##araclar"); // İkon şeridi BeginChild(genişlik=0) kullanır → popup'ın otomatik boyutuna // genişlik KATMAZ (popup daralır, ikonlar kırpılır). Alt sınır ver. ImGui::SetNextWindowSizeConstraints({248.0f, 0.0f}, {FLT_MAX, FLT_MAX}); if (ImGui::BeginPopup("##araclar")) { // Kompakt ikon: ikonButon boyutu GetFrameHeight()+6'dan türer → FramePadding // küçültünce hem butonlar hem şerit yüksekliği birlikte küçülür. ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2{4.0f, 2.0f}); // Kategorili ikon paleti (web tarzı): başlık + neon ikon şeridi. Her buton // bir aç/kapa; aktif durum ikonButon'un neon çerçevesiyle belli olur. const ImVec4 BASLIK = ui::tema().vurgu; auto grupBaslik = [&](const char* s) { ImGui::Dummy({0, 2}); ImGui::TextColored(BASLIK, "%s", s); }; // Aç/kapa ikon butonu (tek satır kullanım): tıklanınca bayrağı çevirir. auto acKapa = [](const char* ikon, const char* ipucu, bool& bayrak) { if (ui::ikonButon(ikon, ipucu, bayrak)) bayrak = !bayrak; }; // MOD: tek tıkla hazır görünüm birleşimleri (durum bayraklardan okunur → hangi // mod aktifse o buton parlar). "Sadece Kütük" = sahnede yalnız oyulmuş kütük: // yol+takım kapalı, tüm-yol asla açılmaz — simülasyonun GÜNCEL durumu görünür. grupBaslik("MOD"); ui::ikonSeritBasla("##mod"); const bool modKutuk = !g_yolGoster && !g_takimGoster; const bool modTam = g_yolGoster && g_takimGoster; if (ui::ikonButon("🧊", "Sadece Kütük: yol ve takım gizli, yalnız oyulmuş kütük", modKutuk)) { g_yolGoster = false; g_takimGoster = false; g_tumYol = false; // bu modda tüm-yol asla devreye girmez } ImGui::SameLine(); if (ui::ikonButon("◈", "Tam: yol + takım birlikte", modTam)) { g_yolGoster = true; g_takimGoster = true; } ui::ikonSeritBitir(); grupBaslik("GÖRÜNÜM"); ui::ikonSeritBasla("##gorunum"); acKapa("👁", "Sahnede göster (ana anahtar)", g_sahneGoster); ImGui::SameLine(); acKapa("〰", "Takım yolunu göster/gizle", g_yolGoster); ImGui::SameLine(); acKapa("⌁", "Tüm yolu göster (kapalı = yalnız geçilen kısım)", g_tumYol); ImGui::SameLine(); acKapa("🔧", "Takımı göster/gizle", g_takimGoster); ImGui::SameLine(); acKapa("⋯", "Yol noktalarını göster", g_noktaGoster); ImGui::SameLine(); acKapa("🎨", "Tek renk yol (kapalı = hareket türü rengi)", g_tekRenk); ImGui::SameLine(); acKapa("⬆", "Çizgiyi derinliksiz önde çiz", g_cizgiOnde); ui::ikonSeritBitir(); grupBaslik("KAMERA"); ui::ikonSeritBasla("##kamera"); acKapa("🎥", "Kamera hareketli takımı izlesin", g_kameraTakip); ImGui::SameLine(); // Yola sığdır: kütük KURULMAMIŞ olsa da çalışır (yol noktalarından AABB). if (ui::ikonButon("⛶", "Takım yolunu ekrana sığdır")) { Aabbf ys; for (const SimNokta& sn : g_prog.noktalar) ys.genislet({(f32)sn.p.x, (f32)sn.p.y, (f32)sn.p.z}); if (ys.gecerliMi()) { const Vec3f bo = ys.boyut(); const f32 yari = std::max(0.5f * std::sqrt(bo.x * bo.x + bo.y * bo.y + bo.z * bo.z), 1.0f); b.kamera.sigdir(ys.merkez(), yari * 1.05f); // %5 pay → kenarlar sıkışmasın } } ImGui::SameLine(); if (ui::ikonButon("⤢", "Tüm sahneyi sığdır") && b.tumunuSigdir) b.tumunuSigdir(); ui::ikonSeritBitir(); grupBaslik("EFEKTLER"); ui::ikonSeritBasla("##efekt"); acKapa("🔊", "İşleme sesi", g_ses); ImGui::SameLine(); acKapa("➤", "Çizgi üzerinde kayarak ilerleme", g_cizgiUzerinde); ui::ikonSeritBitir(); grupBaslik("ARALIK"); ui::ikonSeritBasla("##aralik"); if (ui::ikonButon("↺", "Oynatma aralığını sıfırla")) { g_pozMin = 0; g_pozMax = -1; } ImGui::SameLine(); if (ui::ikonButon("⚙", "Simülasyon motoru penceresi", g_motorPencereAcik)) g_motorPencereAcik = !g_motorPencereAcik; ui::ikonSeritBitir(); ImGui::PopStyleVar(); // FramePadding (kompakt ikon) ImGui::EndPopup(); } ImGui::SameLine(); if (ImGui::Button("Sim. Motoru")) g_motorPencereAcik = !g_motorPencereAcik; // Araçlar sağı ImGui::SetItemTooltip("Kütük/talaş kaldırma motoru penceresini aç/kapat"); ImGui::SameLine(); { const char* kb = "Kapat"; const float kw = ImGui::CalcTextSize(kb).x + ImGui::GetStyle().FramePadding.x * 2.0f; const float off = ImGui::GetContentRegionAvail().x - kw; if (off > 0.0f) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + off); if (ImGui::Button(kb)) acik = false; } if (g_pickHedef >= 0) ImGui::TextColored(CYAN, "Sahnede yol noktasına tıkla (ESC iptal)"); } } // Simülasyon Motoru — ayrı pencere (ana pencereden bağımsız çizilir, web referansı). simMotorPenceresiCiz(sahne, cam); if (!acik) { // kapanınca sahne düğümlerini kaldır + sesi sustur + motor penceresini kapat g_simAcik = false; g_motorPencereAcik = false; g_kutukAsync.durdur(); // worker thread'i join et (kapanışta askıda kalmasın) simSahneTemizle(sahne); SimSes::instance().kapat(); } if (!govde) return; // pencere katlı → oynatma ilerlemez (özgün davranış korunur) // ---- 3B nokta-pick (web fikoRayCast eşleniği): yol noktalarını ekrana izdüşür, // fareye en yakını vurgula; tık → hedefe uygula. Yalnız kip aktifken O(N). if (g_pickHedef >= 0 && !g_prog.noktalar.empty()) { ImGuiIO& io = ImGui::GetIO(); if (ImGui::IsKeyPressed(ImGuiKey_Escape)) { g_pickHedef = -1; } else if (io.DisplaySize.x > 8.0f && io.DisplaySize.y > 8.0f) { const ImVec2 ds = io.DisplaySize; const Mat4f vp = b.kamera.izdusum(ds.x / ds.y) * b.kamera.gorunum(); int enIyi = -1; f32 enIyiUzKare = 18.0f * 18.0f; // px eşiği (web raycast threshold eşleniği) ImVec2 enIyiPx{}; for (size_t i = 0; i < g_prog.noktalar.size(); ++i) { const Vec3d& p = g_prog.noktalar[i].p; const Vec4f h = vp * Vec4f{Vec3f{(f32)p.x, (f32)p.y, (f32)p.z}, 1.0f}; if (h.w <= 1e-6f) continue; // kamera arkası const f32 zn = h.z / h.w; if (zn < -0.05f || zn > 1.05f) continue; // reverse-Z near/far dışı const f32 px = (h.x / h.w * 0.5f + 0.5f) * ds.x; const f32 py = (h.y / h.w * 0.5f + 0.5f) * ds.y; const f32 dx = px - io.MousePos.x, dy = py - io.MousePos.y; const f32 uz = dx * dx + dy * dy; if (uz < enIyiUzKare) { enIyiUzKare = uz; enIyi = (int)i; enIyiPx = {px, py}; } } ImDrawList* fg = ImGui::GetForegroundDrawList(); const char* hedefAd = g_pickHedef == 0 ? "Başlangıç" : g_pickHedef == 1 ? "Şimdiki" : "Bitiş"; fg->AddText({io.MousePos.x + 14, io.MousePos.y + 14}, IM_COL32(26, 217, 255, 255), hedefAd); if (enIyi >= 0) { fg->AddCircle(enIyiPx, 7.0f, IM_COL32(255, 255, 255, 230), 0, 2.0f); fg->AddCircleFilled(enIyiPx, 3.0f, IM_COL32(26, 217, 255, 255)); if (!io.WantCaptureMouse && ImGui::IsMouseClicked(0)) { if (g_pickHedef == 0) { g_pozMin = enIyi; if (g_pozMax >= 0 && g_pozMax < g_pozMin) g_pozMax = -1; } else if (g_pickHedef == 1) { g_poz = enIyi; } else { g_pozMax = enIyi; if (g_pozMin > g_pozMax) g_pozMin = 0; } g_yonu = 0; g_zamanBirikim = 0; g_pickHedef = -1; } } } } // Oynatmayı en son ilerlet (çizim sonrası; sonraki karede yeni poz görünür). oynatIlerlet(); pozKilitle(); const auto tG = std::chrono::steady_clock::now(); simSahneGuncelle(sahne, cam); // geçilen yol aralığı (O(1)) + hareketli takım perfGosterge().simGuncelleMs = std::chrono::duration(std::chrono::steady_clock::now() - tG).count(); // ---- Kamera takip + işleme sesi (oynatma parametrelerinden) ---- if (!g_prog.noktalar.empty()) { const SimNokta& n = g_prog.noktalar[(size_t)g_poz]; // Kamera takip: odağı takım ucuna yumuşak kaydır (orbit korunur, sadece merkez). if (g_kameraTakip) b.kamera.merkezle({(f32)n.p.x, (f32)n.p.y, (f32)n.p.z}); // Ses: oynarken iğ whir'i (devir→frekans) + kesme gıcırtısı; dururken sus. if (g_ses) { SimSes::instance().baslat(); const bool calisiyor = g_yonu != 0; const bool kesme = n.tur == HareketTuru::Isleme || n.tur == HareketTuru::Yaklasma; f32 freq = 90.0f + (f32)std::min(n.devir * 0.02, 1200.0); // rpm→duyulabilir ton f32 amp = 0.0f; if (calisiyor) { amp = kesme ? 0.55f : 0.20f; const f32 fOran = (f32)std::min(std::max(n.hiz / 2000.0, 0.4), 1.0); // ilerleme etkisi amp *= fOran; } SimSes::instance().ayarla(freq, amp, kesme); } else if (SimSes::instance().acikMi()) { SimSes::instance().kapat(); } } // Kütük oyma: "Sona Kadar Oy" isteği veya ▶ Sim ile Oyma — ikisi de kare başına // zaman bütçeli; mesh çıkarma süreyle kapılanır (eski her-kare çıkarma kasıyordu). perfGosterge().simKutukMs = 0.0; perfGosterge().simKutukMeshMs = 0.0; const int son2 = (int)g_prog.noktalar.size() - 1; const bool carveIstek = g_kutuk.hedefIstek >= 0 || g_kutuk.otoOy; // Tercih kapatıldıysa çalışan async'i durdur (grid kaybolur; kullanıcı yeniden Kürs). if (!g_kutukAsyncTercih && g_kutukAsync.calisiyorMu()) g_kutukAsync.durdur(); // İlk carve isteğinde async'i başlat (motoru worker'a devret). GPU'da başlamaz. if (g_kutuk.kuruldu && g_kutukAsyncTercih && !g_kutuk.gpu && carveIstek && !g_kutukAsync.calisiyorMu() && g_kutuk.motor) kutukAsyncBaslat(cam); if (g_kutukAsync.calisiyorMu()) { // ---- ASYNC yol: hedef ata (worker oyar) + hazır mesh poll → GPU upload ----- int hedef = -1; bool kismi = false; // interpolasyon: yalnız replay + çizgi üzerinde kayma if (g_kutuk.hedefIstek >= 0) { hedef = std::min(g_kutuk.hedefIstek, son2); // "Sona Kadar Oy": tam hamle, interpolasyon yok } else if (g_kutuk.otoOy) { hedef = std::min(g_poz, son2); kismi = g_cizgiUzerinde && g_yonu != 0; // takım çizgi üzerinde kayıyor } if (hedef >= 0) { // Geri-scrub ARTIK hard-reset DEĞİL — worker en yakın checkpoint'ten O(1) yükler. // Interpolasyonlu takım ucu → kütük boşaltma ucu takip eder (kismi swept). g_kutukAsync.hedefAta(hedef, kismi ? takimKonumGuncel() : Vec3d{}, kismi); } g_kutuk.oyulanPoz = g_kutukAsync.oyulanPoz(); g_kutuk.hacimMm3 = g_kutukAsync.hacim(); if (g_kutuk.hedefIstek >= 0 && g_kutuk.oyulanPoz >= std::min(g_kutuk.hedefIstek, son2)) g_kutuk.hedefIstek = -1; MeshVeri veri; if (g_kutukAsync.meshDevral(veri)) { const auto tM = std::chrono::steady_clock::now(); kutukAsyncMeshYukle(sahne, std::move(veri)); perfGosterge().simKutukMeshMs = std::chrono::duration(std::chrono::steady_clock::now() - tM).count(); } } else if (g_kutuk.kuruldu && g_kutuk.motor) { // ---- SENKRON yol (async kapalı/GPU): eski davranış birebir korunur --------- const auto tK = std::chrono::steady_clock::now(); if (g_kutuk.hedefIstek >= 0) { kutukBatchOy(cam, g_kutuk.hedefIstek); if (g_kutuk.oyulanPoz >= std::min(g_kutuk.hedefIstek, son2)) g_kutuk.hedefIstek = -1; } else if (g_kutuk.otoOy) { kutukBatchOy(cam, g_poz); } perfGosterge().simKutukMs = std::chrono::duration(std::chrono::steady_clock::now() - tK).count(); if (g_kutuk.meshBayat) { // Oyma bitti (hedefe ulaşıldı) → hemen; sürüyor → g_kutukMeshMs aralığıyla. const bool suruyor = g_kutuk.hedefIstek >= 0 || (g_kutuk.otoOy && g_kutuk.oyulanPoz < std::min(g_poz, son2)); const f32 gecenMs = std::chrono::duration( std::chrono::steady_clock::now() - g_kutukSonMesh).count(); if (!suruyor || gecenMs >= g_kutukMeshMs) { const auto tM = std::chrono::steady_clock::now(); kutukMeshYenile(sahne); perfGosterge().simKutukMeshMs = std::chrono::duration(std::chrono::steady_clock::now() - tM).count(); } } } } bool camSimSahneGorselAl() { return g_sahneGoster; } void camSimSahneGorselAta(bool v) { g_sahneGoster = v; } bool camSimTumYolAl() { return g_tumYol; } void camSimTumYolAta(bool v) { g_tumYol = v; } bool camSimYolGosterAl() { return g_yolGoster; } void camSimYolGosterAta(bool v) { g_yolGoster = v; } bool camSimTakimGosterAl() { return g_takimGoster; } void camSimTakimGosterAta(bool v) { g_takimGoster = v; } f32 camSimKutukBatchMsAl() { return g_kutukBatchMs; } void camSimKutukBatchMsAta(f32 v) { g_kutukBatchMs = v; } f32 camSimKutukMeshMsAl() { return g_kutukMeshMs; } void camSimKutukMeshMsAta(f32 v) { g_kutukMeshMs = v; } bool camSimSesAl() { return g_ses; } void camSimSesAta(bool v) { g_ses = v; } bool camSimKameraTakipAl() { return g_kameraTakip; } void camSimKameraTakipAta(bool v) { g_kameraTakip = v; } u32 camSimRenkEsikAl() { return g_renkEsik; } void camSimRenkEsikAta(u32 v) { g_renkEsik = v; } u32 camSimKutukMaksHucreAl() { return g_kutukMaksHucre; } void camSimKutukMaksHucreAta(u32 v) { g_kutukMaksHucre = v; } bool camSimKutukTakimUzatAl() { return g_kutukTakimUzat; } void camSimKutukTakimUzatAta(bool v) { g_kutukTakimUzat = v; } bool camSimKutukAsyncAl() { return g_kutukAsyncTercih; } void camSimKutukAsyncAta(bool v) { g_kutukAsyncTercih = v; } bool camSimKutukKeskinAl() { return g_kutukKeskin; } void camSimKutukKeskinAta(bool v) { g_kutukKeskin = v; g_kutukAsync.keskinAta(v); if (g_kutuk.motor) { g_kutuk.motor->keskinMeshAta(v); g_kutuk.meshBayat = true; } } } // namespace fiko