#include "dosya_islemleri.hpp" #include "kinematik_io.hpp" // mekanizmaCikar / mekanizmaUygula (v1.17 KNMT) #include "malzeme_io.hpp" #include "dogrudan_ayar.hpp" // importAyriBody (STEP → ayrı body opsiyonu) #include "ayar_kayitlari.hpp" // aptIthalSimulasyon (APT ithali → sim penceresi) #include "cam_simulasyon_penceresi.hpp" // camSimPencereAc (APT ithal simülasyonu) #include "oto_kayit.hpp" // aç/kaydet → kurtarma metası #include "lisans_yukle.hpp" // lisansSaglikKontrol (dağınık kurcalama kontrol noktası) #include #include #include #include #include #include #include #include #include #include #include #include #include "uygulama_baglam.hpp" #include "fiko/cam/apt.hpp" #include "fiko/cekirdek/olay.hpp" // AptYazildi (async finalize yayını) #include "fiko/cam/cam_sistemi.hpp" #include "fiko/render/orbit_kamera.hpp" // APT ithalinde kamerayı yola sığdır (sigdir) #include "fiko/io/fiko_format.hpp" #include "fiko/io/web_io.hpp" // FikoOp/WRL okuyucu + ithalSonucuKur #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/cekirdek/gecici.hpp" // merkezi geçici/önbellek dizini (kaynak yanına yazma yok) #include "fiko/cekirdek/gorev.hpp" // GorevSistemi/GorevDurum (async dosya işi) #include "fiko/core/gunluk.hpp" #include "fiko/platform/pencere.hpp" #include "fiko/render/vertex.hpp" #include "fiko/render/vulkan_baglam.hpp" // toplu yükleme oturumu (import perf) #include "fiko/sahne/sahne.hpp" #include "fiko/ui/agac_simge_kayit.hpp" // öğe-bazlı simge override kalıcılığı (v1.5 ASMG) namespace fiko { static void govdeleriAsenkronAktar(UygulamaBaglam& b, const std::string& s, std::string basariMesaj, std::string hataMesaj); // Yol → uzantısız dosya adı ("C:\\a\\parca.step" → "parca"). Süreç kaydı belge etiketi. static std::string dosyaTabanAdi(const std::string& yol) { std::string ad = yol; if (const size_t s = ad.find_last_of("/\\"); s != std::string::npos) ad = ad.substr(s + 1); if (const size_t d = ad.find_last_of('.'); d != std::string::npos) ad = ad.substr(0, d); return ad; } std::string dosyaSec(HWND pencere, bool kaydet) { wchar_t yol[MAX_PATH] = {}; OPENFILENAMEW ofn{}; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = pencere; ofn.lpstrFilter = L"STEP Dosyaları (*.step;*.stp)\0*.step;*.stp\0Tümü (*.*)\0*.*\0"; ofn.lpstrFile = yol; ofn.nMaxFile = MAX_PATH; ofn.lpstrDefExt = L"step"; ofn.Flags = kaydet ? (OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST) : (OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST); const BOOL tamam = kaydet ? GetSaveFileNameW(&ofn) : GetOpenFileNameW(&ofn); if (!tamam) return {}; const int bayt = WideCharToMultiByte(CP_UTF8, 0, yol, -1, nullptr, 0, nullptr, nullptr); std::string s(bayt - 1, 0); WideCharToMultiByte(CP_UTF8, 0, yol, -1, s.data(), bayt, nullptr, nullptr); return s; } // Görüntü/simge dosyası seç (aç). Ağaç tip-simgeleri ve öğe override için. std::string resimDosyaSec(HWND pencere) { wchar_t yol[MAX_PATH] = {}; OPENFILENAMEW ofn{}; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = pencere; ofn.lpstrFilter = L"Simge (*.svg;*.png;*.jpg;*.jpeg;*.bmp;*.gif)\0*.svg;*.png;*.jpg;*.jpeg;*.bmp;*.gif\0" L"Tümü (*.*)\0*.*\0"; ofn.lpstrFile = yol; ofn.nMaxFile = MAX_PATH; ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; if (!GetOpenFileNameW(&ofn)) return {}; const int bayt = WideCharToMultiByte(CP_UTF8, 0, yol, -1, nullptr, 0, nullptr, nullptr); std::string s(bayt - 1, 0); WideCharToMultiByte(CP_UTF8, 0, yol, -1, s.data(), bayt, nullptr, nullptr); return s; } // APT/CL (.FikoApt/.apt) dosya seç (aç veya kaydet). dosyaSec kalıbı; ayrı // filtre + varsayılan uzantı (.FikoApt birincil, .apt ikincil). std::string aptDosyaSec(HWND pencere, bool kaydet) { wchar_t yol[MAX_PATH] = {}; OPENFILENAMEW ofn{}; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = pencere; ofn.lpstrFilter = L"APT / CL (*.FikoApt;*.apt)\0*.FikoApt;*.apt\0Tümü (*.*)\0*.*\0"; ofn.lpstrFile = yol; ofn.nMaxFile = MAX_PATH; ofn.lpstrDefExt = kaydet ? L"apt" : nullptr; ofn.Flags = kaydet ? (OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST) : (OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST); const BOOL tamam = kaydet ? GetSaveFileNameW(&ofn) : GetOpenFileNameW(&ofn); if (!tamam) return {}; const int bayt = WideCharToMultiByte(CP_UTF8, 0, yol, -1, nullptr, 0, nullptr, nullptr); std::string s(bayt - 1, 0); WideCharToMultiByte(CP_UTF8, 0, yol, -1, s.data(), bayt, nullptr, nullptr); return s; } // Web dosyası seç (aç): .FikoOp (Fikoperasyon: mesh + takım yolu) + .wrl (VRML97 // mesh). dosyaSec kalıbı; birleşik filtre, uzantıya göre yolAc dağıtır. std::string webDosyaSec(HWND pencere) { wchar_t yol[MAX_PATH] = {}; OPENFILENAMEW ofn{}; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = pencere; ofn.lpstrFilter = L"Web CAD (*.FikoOp;*.wrl)\0*.FikoOp;*.wrl\0" L"Fikoperasyon (*.FikoOp)\0*.FikoOp\0VRML (*.wrl)\0*.wrl\0" L"Tümü (*.*)\0*.*\0"; ofn.lpstrFile = yol; ofn.nMaxFile = MAX_PATH; ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; if (!GetOpenFileNameW(&ofn)) return {}; const int bayt = WideCharToMultiByte(CP_UTF8, 0, yol, -1, nullptr, 0, nullptr, nullptr); std::string s(bayt - 1, 0); WideCharToMultiByte(CP_UTF8, 0, yol, -1, s.data(), bayt, nullptr, nullptr); return s; } // Tüm desteklenen içe aktarma türleri için BİRLEŞİK "Aç" diyaloğu (toolbar/menü). // dosyaSec kalıbı; tek filtre grubunda tüm uzantılar + ayrı-ayrı alt filtreler. // Seçilen yolun uzantıya göre yönlendirilmesi yolAc() içinde yapılır. std::string tumDosyaSec(HWND pencere) { wchar_t yol[MAX_PATH] = {}; OPENFILENAMEW ofn{}; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = pencere; ofn.lpstrFilter = L"Tüm Desteklenen Dosyalar (*.fikocad;*.step;*.stp;*.FikoApt;*.apt;*.FikoOp;*.wrl)\0" L"*.fikocad;*.step;*.stp;*.FikoApt;*.apt;*.FikoOp;*.wrl\0" L"FikoCAD Belgesi (*.fikocad)\0*.fikocad\0" L"STEP (*.step;*.stp)\0*.step;*.stp\0" L"APT / CL (*.FikoApt;*.apt)\0*.FikoApt;*.apt\0" L"Fikoperasyon (*.FikoOp)\0*.FikoOp\0" L"VRML (*.wrl)\0*.wrl\0" L"Tümü (*.*)\0*.*\0"; ofn.lpstrFile = yol; ofn.nMaxFile = MAX_PATH; ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; if (!GetOpenFileNameW(&ofn)) return {}; const int bayt = WideCharToMultiByte(CP_UTF8, 0, yol, -1, nullptr, 0, nullptr, nullptr); std::string s(bayt - 1, 0); WideCharToMultiByte(CP_UTF8, 0, yol, -1, s.data(), bayt, nullptr, nullptr); return s; } // STEP içe aktarma ağır (OCCT parse + ağ üretimi) → GorevSistemi'nde worker // thread'de çalışır; parse+mesh saf CPU, sonuç main-thread'de GPU'ya yüklenir. // stepYol: verilen yolu içe aktarır (dialog yok). stepAc dialog gösterip buna delege. static void stepYol(UygulamaBaglam& b, const std::string& yol) { Cekirdek& cekirdek = b.cekirdek; if (yol.empty()) return; if (b.sonDosyaEkle) b.sonDosyaEkle(yol); // MRU kaydı (başarılı seçim → kalıcı) // Dosya adı (uzantısız) → kök grup etiketi ("stp " hissi). std::string dosyaAdi = yol; if (const size_t s = dosyaAdi.find_last_of("/\\"); s != std::string::npos) dosyaAdi = dosyaAdi.substr(s + 1); if (const size_t d = dosyaAdi.find_last_of('.'); d != std::string::npos) dosyaAdi = dosyaAdi.substr(0, d); // Worker'da mesh'lenmiş, montaj hiyerarşisini koruyan hazır düğüm ağacı. struct HazirDugum { std::string ad; bool govdeVar = false; geometri::IthalGovde ig; // yaprak geometri (dünya koordinatında) MeshVeri yuzey; KenarVeri kenar; NoktaVeri nokta; std::vector altlar; }; auto sonuc = std::make_shared>(); cekirdek.gorevler().kuyrukla( "STEP İçe Aktar", [yol, sonuc](GorevBaglam& b) { // WORKER: saf CPU (GPU/sahne/olay yasak) // TESHIS (2026-08-08, "STEP acilmiyor" sikayeti): worker'da yakalanmayan // HERHANGI bir istisna GorevSistemi'nde GorevDurum::Hata'ya duser ve // bitince() (asagidaki FINALIZE, kullaniciya "STEP acilamadi" mesaji // gosteren TEK yer) HIC CAGRILMAZ (bkz. gorev.cpp:pompala — bitince yalniz // Bitti'de calisir) — is UI'da sessizce kaybolur, kullanici hicbir sey // gormez. Butun govdeyi sarip HER istisnayi burada yakala + logla + sonuc'u // BOS birak: boylece is normal (Bitti) doner, FINALIZE calisir ve kullaniciya // gercek nedeni gosterir; ayrica fikocad.log'a da tam detay yazilir. try { // ---- STEP MESH CACHE (.fkcache): ikinci açılış ANINDA. Anahtar = // dosya boyutu + değişim zamanı; format sürümlü. BREP BinTools ile, // mesh'ler ham POD dump ile saklanır → parse+tessellation tamamen atlanır. namespace fs = std::filesystem; // Önbellek dosyası kaynağın YANINA değil, uygulama geçici ağacına yazılır // (gecici.hpp): kullanıcı klasörü kirlenmez, salt-okunur/ağ dizininde de // çalışır, tek yerden budanır. Eski sürümün yan-dosyası varsa taşınır. gecici::eskiYanDosyaGoc(yol, "fkcache"); const std::string cacheYol = gecici::onbellekDosyasi(yol, "fkcache").string(); constexpr u32 CACHE_SURUM = 5; // 5: brep per-yüz renkleri cache'e eklendi (govdeBaytYaz // yalnız shape taşır — renksiz brep ilk it/çek'te tüm renk // sigortalarını devre dışı bırakıp gövdeyi griye düşürüyordu) u64 dosyaBoyut = 0, dosyaZaman = 0; { std::error_code ec; dosyaBoyut = (u64)fs::file_size(yol, ec); const auto z = fs::last_write_time(yol, ec); dosyaZaman = (u64)z.time_since_epoch().count(); } const auto yaz = [](std::ostream& o, const void* p, size_t n) { o.write((const char*)p, (std::streamsize)n); }; const auto oku = [](std::istream& i, void* p, size_t n) { i.read((char*)p, (std::streamsize)n); return (bool)i; }; // Düğüm (de)serileştirme — POD dump + uzunluk önekleri. std::function dugumYaz = [&](std::ostream& o, const HazirDugum& h) { const u32 adN = (u32)h.ad.size(); yaz(o, &adN, 4); yaz(o, h.ad.data(), adN); const u8 gv = h.govdeVar ? 1 : 0; yaz(o, &gv, 1); if (h.govdeVar) { const u8 rv = h.ig.renkVar ? 1 : 0; yaz(o, &rv, 1); yaz(o, &h.ig.renk, sizeof(Vec3f)); const u32 vN = (u32)h.yuzey.vertexler.size(), iN = (u32)h.yuzey.indeksler.size(); yaz(o, &vN, 4); yaz(o, h.yuzey.vertexler.data(), vN * sizeof(Vertex)); yaz(o, &iN, 4); yaz(o, h.yuzey.indeksler.data(), iN * 4); yaz(o, &h.yuzey.sinir, sizeof(Aabbf)); yaz(o, &h.yuzey.yuzSayi, 4); const u32 kN = (u32)h.kenar.vertexler.size(); yaz(o, &kN, 4); yaz(o, h.kenar.vertexler.data(), kN * sizeof(KenarVertex)); yaz(o, &h.kenar.kenarSayi, 4); const u32 nN = (u32)h.nokta.vertexler.size(); yaz(o, &nN, 4); yaz(o, h.nokta.vertexler.data(), nN * sizeof(KenarVertex)); yaz(o, &h.nokta.noktaSayi, 4); const std::string brep = geometri::govdeBaytYaz(h.ig.govde); const u32 bN = (u32)brep.size(); yaz(o, &bN, 4); yaz(o, brep.data(), bN); // Per-yüz brep renkleri (govdeBaytYaz TAŞIMAZ — BinTools yalnız shape yazar). // Bunlar olmadan cache'ten yüklenen brep renksiz kalır → ilk DM işleminde // renk propagasyonu/sigortaları çalışamaz, gövde tek renge düşer. const auto& yr = h.ig.govde.yuzRenkleri(); const u32 yrN = h.ig.govde.yuzRenkliMi() ? (u32)yr.size() : 0; yaz(o, &yrN, 4); if (yrN) yaz(o, yr.data(), yrN * sizeof(Vec3f)); } const u32 aN = (u32)h.altlar.size(); yaz(o, &aN, 4); for (const auto& c : h.altlar) dugumYaz(o, c); }; std::function dugumOku = [&](std::istream& i, HazirDugum& h) -> bool { u32 adN = 0; if (!oku(i, &adN, 4) || adN > 4096) return false; h.ad.resize(adN); if (adN && !oku(i, h.ad.data(), adN)) return false; u8 gv = 0; if (!oku(i, &gv, 1)) return false; h.govdeVar = gv != 0; if (h.govdeVar) { u8 rv = 0; if (!oku(i, &rv, 1) || !oku(i, &h.ig.renk, sizeof(Vec3f))) return false; h.ig.renkVar = rv != 0; u32 vN = 0, iN = 0; if (!oku(i, &vN, 4)) return false; h.yuzey.vertexler.resize(vN); if (vN && !oku(i, h.yuzey.vertexler.data(), vN * sizeof(Vertex))) return false; if (!oku(i, &iN, 4)) return false; h.yuzey.indeksler.resize(iN); if (iN && !oku(i, h.yuzey.indeksler.data(), iN * 4)) return false; if (!oku(i, &h.yuzey.sinir, sizeof(Aabbf)) || !oku(i, &h.yuzey.yuzSayi, 4)) return false; u32 kN = 0; if (!oku(i, &kN, 4)) return false; h.kenar.vertexler.resize(kN); if (kN && !oku(i, h.kenar.vertexler.data(), kN * sizeof(KenarVertex))) return false; if (!oku(i, &h.kenar.kenarSayi, 4)) return false; u32 nN = 0; if (!oku(i, &nN, 4)) return false; h.nokta.vertexler.resize(nN); if (nN && !oku(i, h.nokta.vertexler.data(), nN * sizeof(KenarVertex))) return false; if (!oku(i, &h.nokta.noktaSayi, 4)) return false; u32 bN = 0; if (!oku(i, &bN, 4)) return false; std::string brep(bN, 0); if (bN && !oku(i, brep.data(), bN)) return false; h.ig.govde = geometri::govdeBaytOku(brep); if (!h.ig.govde.gecerliMi()) return false; u32 yrN = 0; if (!oku(i, &yrN, 4) || yrN > 10000000) return false; if (yrN) { std::vector yr(yrN); if (!oku(i, yr.data(), yrN * sizeof(Vec3f))) return false; h.ig.govde.yuzRenkleriAta(std::move(yr)); } } u32 aN = 0; if (!oku(i, &aN, 4) || aN > 100000) return false; h.altlar.resize(aN); for (auto& c : h.altlar) if (!dugumOku(i, c)) return false; return true; }; // Cache dene: başlık eşleşirse parse+mesh tamamen atlanır. { std::ifstream cf(cacheYol, std::ios::binary); if (cf) { char magic[4] = {}; u32 surum = 0; u64 cb = 0, cz = 0, kokN = 0; if (oku(cf, magic, 4) && std::memcmp(magic, "FKC1", 4) == 0 && oku(cf, &surum, 4) && surum == CACHE_SURUM && oku(cf, &cb, 8) && cb == dosyaBoyut && oku(cf, &cz, 8) && cz == dosyaZaman && oku(cf, &kokN, 8) && kokN < 100000) { b.ilerlet(0.2f, "Önbellekten yükleniyor"); std::vector ck((size_t)kokN); bool tamam = true; for (auto& k : ck) if (!dugumOku(cf, k)) { tamam = false; break; } if (tamam) { *sonuc = std::move(ck); gunluk::bilgi("STEP önbellekten yüklendi (anında): {}", yol); b.ilerlet(1.0f, "Önbellekten yüklendi"); return; } sonuc->clear(); gunluk::uyari("STEP önbellek bozuk, yeniden üretilecek: {}", cacheYol); } } } const auto parseBasla = std::chrono::steady_clock::now(); // WORKER - DEBUG EKLENDİ: STEP dosyası işleniyor gunluk::bilgi("WORKER: STEP dosyası işleniyor: {}", yol); auto kokler = geometri::stepYukleHiyerarsi( // yol, [&](f32 o, std::string_view m) { b.ilerlet(o, m); }); yol, [&](f32 o, std::string_view m) { b.ilerlet(o, m); gunluk::bilgi("PARSE {}%: {}", (int)(o*100), m); }); // WORKER - DEBUG EKLENDİ: kok sayısını raporla if (kokler.empty()) { gunluk::hata("WORKER HATASI: stepYukleHiyerarsi bos sonuc dondurdu!"); } else { gunluk::bilgi("WORKER: {} kok atomik bulundu", kokler.size()); } const f64 parseMs = std::chrono::duration( std::chrono::steady_clock::now() - parseBasla).count(); gunluk::bilgi("STEP parse: {:.0f} ms", parseMs); // Yaprak (mesh'lenecek gövde) sayısını say → ilerleme oranı. std::function yaprakSay = [&](const geometri::IthalDugum& d) { int s = d.govdeVar ? 1 : 0; for (const auto& c : d.altlar) s += yaprakSay(c); return s; }; int toplam = 0; for (const auto& d : kokler) toplam += yaprakSay(d); // FAZ 1 — iskelet: ağacı mesh'lemeden kur, gövdeyi taşı, yaprakları topla. std::vector yapraklar; yapraklar.reserve((size_t)toplam); std::function iskelet = [&](geometri::IthalDugum& d) -> HazirDugum { HazirDugum h; h.ad = std::move(d.ad); h.govdeVar = d.govdeVar; if (d.govdeVar && d.govde.gecerliMi()) { h.ig.govde = std::move(d.govde); h.ig.renkVar = d.renkVar; h.ig.renk = d.renk; } else { h.govdeVar = false; } h.altlar.reserve(d.altlar.size()); for (auto& c : d.altlar) h.altlar.push_back(iskelet(c)); return h; }; sonuc->reserve(kokler.size()); for (auto& d : kokler) sonuc->push_back(iskelet(d)); std::function yaprakTopla = [&](HazirDugum& h) { if (h.govdeVar) yapraklar.push_back(&h); for (auto& c : h.altlar) yaprakTopla(c); }; for (auto& h : *sonuc) yaprakTopla(h); // FAZ 2 — gövdeler-arası PARALEL tessellation. // GÜVENLİK: STEP'ten bölünen kardeş gövdeler altta AYNI Geom yüzeylerini // paylaşır; paylaşımlı geometriyi iki thread'de mesh'lemek data-race/çökme // yapar. Bu yüzden önce SERİ derin kopya (paylaşım kopar, ucuz), sonra // her thread kendi ÖZEL geometrisini mesh'ler (tamamen güvenli). const auto meshBasla = std::chrono::steady_clock::now(); std::atomic kuyruk{0}, biten{0}; const unsigned isciSayi = std::min( std::max(1u, std::thread::hardware_concurrency() - 1u), (unsigned)std::max(yapraklar.size(), 1)); if (isciSayi > 1 && yapraklar.size() > 1) { size_t k = 0; for (HazirDugum* h : yapraklar) { if (b.iptalIstendi()) return; geometri::BrepGovde kopya = geometri::govdeDerinKopya(h->ig.govde); if (kopya.gecerliMi()) h->ig.govde = std::move(kopya); // Kopya başarısızsa orijinal kalır → o gövde aşağıda TEK thread'de // (kuyruğun sonunda seri) işlense de paylaşım riski kalıyor olabilir; // pratikte Copy başarısızlığı ≈ geçersiz gövde, mesh de atlanır. if (toplam > 0) b.ilerlet(0.44f + 0.06f * (f32)(++k) / (f32)toplam, "Geometri ayrıştırılıyor"); } } // Paralel BRepMesh YALNIZ tek gövdede (dış paralellik yok → OSD_ThreadPool // nesting/deadlock oluşmaz). Çok gövdede zaten gövde-başına thread var, iç // paralel nested-pool deadlock'u tetikler → seri. Ölçüldü: tek gövde 5.3× hız. const bool meshParalel = importParalelMesh() && yapraklar.size() == 1; const auto isci = [&] { // Bu lambda'yı çalıştıran her std::thread OCCT kullanır (ucgenle/kenarMesh/ // noktaMesh) ama GorevSistemi'nin worker-başlatma kancasından (main.cpp: // isciBaslatmaKancasiAyarla) GEÇMEZ → OSD::SetSignal kurulmamış olur. Bazı // STEP'lerde (bozuk/uç-durum geometri) mesh/kenar üretimi sırasında OCCT bir // access violation atarsa bu SEH try/catch'e YAKALANMAZ ve süreç sessizce // düşer ("bazı STEP'ler açılmıyor" kök nedeni). Her thread'de BİR KEZ kur. geometri::occtSinyalKur(); for (;;) { const size_t i = kuyruk.fetch_add(1); if (i >= yapraklar.size() || b.iptalIstendi()) return; HazirDugum& h = *yapraklar[i]; const Vec3f renk = h.ig.renkVar ? h.ig.renk : Vec3f{0.62f, 0.66f, 0.72f}; h.yuzey = h.ig.govde.ucgenle(renk, -1.0, meshParalel); h.kenar = h.ig.govde.kenarMesh(); h.nokta = h.ig.govde.noktaMesh(); h.ig.govde.yuzBilgiOnbellekKur(); // akıllı seçim cache'i WORKER'da (seçimde tıkanma yok) const size_t n = biten.fetch_add(1) + 1; if (toplam > 0) b.ilerlet(0.5f + 0.5f * (f32)n / (f32)toplam, "Ağ üretiliyor (paralel)"); } }; std::vector isciler; isciler.reserve(isciSayi); for (unsigned t = 0; t < isciSayi; ++t) isciler.emplace_back(isci); for (auto& t : isciler) t.join(); const f64 meshMs = std::chrono::duration( std::chrono::steady_clock::now() - meshBasla).count(); gunluk::bilgi("STEP ağ üretimi: {} gövde, {} işçi, {:.0f} ms", yapraklar.size(), isciSayi, meshMs); // Cache yaz (geçici dosya + rename → yarım dosya asla geçerli olmaz). if (!b.iptalIstendi() && !sonuc->empty()) { const std::string gecici = cacheYol + ".tmp"; std::ofstream cf(gecici, std::ios::binary | std::ios::trunc); if (cf) { yaz(cf, "FKC1", 4); yaz(cf, &CACHE_SURUM, 4); yaz(cf, &dosyaBoyut, 8); yaz(cf, &dosyaZaman, 8); const u64 kokN = (u64)sonuc->size(); yaz(cf, &kokN, 8); for (const auto& k : *sonuc) dugumYaz(cf, k); cf.close(); std::error_code ec; fs::rename(gecici, cacheYol, ec); if (ec) fs::remove(gecici, ec); else gunluk::bilgi("STEP önbelleği yazıldı: {}", cacheYol); } } } catch (const std::exception& e2) { gunluk::hata("STEP WORKER istisnasi (std::exception) — dosya: {} — mesaj: {}", yol, e2.what()); sonuc->clear(); } catch (...) { // NOT: OCCT'nin Standard_Failure'i bu ceviri biriminde gorunmuyor (fikocad hedefi // OCCT include yolunu almiyor — yalniz fiko_geometri kutuphanesi aliyor), bu yuzden // OCCT ic hatalari (Standard_Failure) da buraya duser — mesaj metni yok ama en azindan // is artik SESSIZCE KAYBOLMUYOR, loglaniyor ve FINALIZE kullaniciya haber veriyor. gunluk::hata("STEP WORKER istisnasi (OCCT Standard_Failure ya da bilinmeyen tur) — " "dosya: {} — muhtemelen bozuk/uc-durum geometri ya da yakalanamayan " "donanim sinyali (occtSinyalKur bu is parcaciginda kurulu olmayabilir)", yol); sonuc->clear(); } }, [&b, sonuc, dosyaAdi, yol]() { // FINALIZE: main-thread (GPU yükleme + sahne + olay) if (sonuc->empty()) { b.bilgiVer("STEP dosyası açılamadı. Dosya: " + yol); return; } // Toplu GPU yükleme oturumu: tüm gövde tamponları TEK submit + TEK // fence-wait (per-tampon stall'lar silinir — büyük montajda 5-20×). b.sahne.vulkan().bekle(); // oturum başı bir kez (per-düğüm değil) b.sahne.vulkan().topluYuklemeBasla(); // Montaj ağacı Part altında bir grup olur (CATIA: import Part içine girer, kök değil). int govdeSay = 0; std::function kur = [&](HazirDugum& h, Varlik ust) { if (!h.altlar.empty()) { const Varlik g = b.sahne.grupEkle(h.ad.empty() ? "Grup" : h.ad, ust); if (h.govdeVar) { Dugum& d = b.sahne.ithalEkleHazir(std::move(h.ig), std::move(h.yuzey), std::move(h.kenar), std::move(h.nokta), g); if (!h.ad.empty()) d.ad = h.ad; ++govdeSay; } for (auto& c : h.altlar) kur(c, g); } else if (h.govdeVar) { Dugum& d = b.sahne.ithalEkleHazir(std::move(h.ig), std::move(h.yuzey), std::move(h.kenar), std::move(h.nokta), ust); if (!h.ad.empty()) d.ad = h.ad; ++govdeSay; } }; // CATIA: içe aktarılan STEP body-scope olur (In Work Object). importAyriBody + çok top- // level katı → HER biri AYRI PartBody (assembly); aksi halde hepsi tek import-body'sinde. if (dogrudanAyar().importAyriBody && sonuc->size() > 1) { Varlik sonBody = VARLIK_YOK; int idx = 0; for (auto& h : *sonuc) { const std::string ad = h.ad.empty() ? (dosyaAdi + "." + std::to_string(++idx)) : h.ad; const Varlik bg = b.sahne.grupEkle(ad, b.sahne.partKokAl()); b.sahne.dunya().ekle(bg, SimgeBilesen{"📦"}); b.sahne.bodyKaydet(bg); kur(h, bg); sonBody = bg; } if (sonBody != VARLIK_YOK) b.sahne.aktifBodyAyarla(sonBody); } else { const Varlik kokGrup = b.sahne.grupEkle(dosyaAdi, b.sahne.partKokAl()); b.sahne.dunya().ekle(kokGrup, SimgeBilesen{"📦"}); b.sahne.bodyKaydet(kokGrup); b.sahne.aktifBodyAyarla(kokGrup); for (auto& h : *sonuc) kur(h, kokGrup); } b.sahne.vulkan().topluYuklemeBitir(); // TEK submit + fence (GPU'ya boşalt) b.tumunuSigdir(); if (b.belgeAdiAyarla) b.belgeAdiAyarla(dosyaAdi); // süreç yöneticisi kaydına belge adı b.bilgiVer(std::to_string(govdeSay) + " gövde içe aktarıldı"); }); } void stepAc(UygulamaBaglam& b) { stepYol(b, dosyaSec((HWND)b.pencere.dogalTutamac(), false)); } void stepKaydetKomut(UygulamaBaglam& b) { lisansSaglikKontrol(); // kontrol noktası #2 (dışa aktarma yolu) const std::string yol = dosyaSec((HWND)b.pencere.dogalTutamac(), true); if (yol.empty()) return; govdeleriAsenkronAktar(b, yol, "STEP kaydedildi", "STEP kaydedilemedi"); } // ---- Yerel .fikocad belgesi (tam durum + kamera) --------------------------- // .fikocad dosya seç (aç/kaydet) — özel filtre + varsayılan uzantı. static std::string fikoDosyaSec(HWND pencere, bool kaydet) { wchar_t yol[MAX_PATH] = {}; OPENFILENAMEW ofn{}; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = pencere; ofn.lpstrFilter = L"FikoCAD Belgesi (*.fikocad)\0*.fikocad\0Tümü (*.*)\0*.*\0"; ofn.lpstrFile = yol; ofn.nMaxFile = MAX_PATH; ofn.lpstrDefExt = L"fikocad"; ofn.Flags = kaydet ? (OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST) : (OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST); const BOOL tamam = kaydet ? GetSaveFileNameW(&ofn) : GetOpenFileNameW(&ofn); if (!tamam) return {}; const int bayt = WideCharToMultiByte(CP_UTF8, 0, yol, -1, nullptr, 0, nullptr, nullptr); std::string s(bayt - 1, 0); WideCharToMultiByte(CP_UTF8, 0, yol, -1, s.data(), bayt, nullptr, nullptr); return s; } // Aynı başlıklı dosya işi zaten kuyruktaysa (Bekliyor/Çalışıyor) → çift-iş guard. // main.cpp'ye dokunmadan GorevSistemi::anlik() başlıklarına bakar (MDI-güvenli). static bool dosyaIsiAktif(Cekirdek& cekirdek, const char* baslik) { for (const GorevGorunum& g : cekirdek.gorevler().anlik()) if (g.baslik == baslik && (g.durum == GorevDurum::Bekliyor || g.durum == GorevDurum::Calisiyor)) return true; return false; } // Verilen .fikocad yolunu ASENKRON yükle (dialog yok) — sürükle-bırak / palet / yolAc. // STEP import kalıbı: worker read+inflate+parse+TESSELLATE (donma buradaydı), finalize // GPU yükleme + sahne kurulum + kamera. "İşlemler" paneli ilerlemeyi otomatik gösterir. static void fikoYol(UygulamaBaglam& b, const std::string& yol) { if (yol.empty()) return; Cekirdek& cekirdek = b.cekirdek; if (dosyaIsiAktif(cekirdek, "Belge Açılıyor")) { b.bilgiVer("Belge zaten açılıyor, lütfen bekleyin…"); return; } auto sonuc = std::make_shared(); auto hata = std::make_shared(); auto tamam = std::make_shared(false); auto simgeler = std::make_shared>>(); // öğe simge override (v1.5) cekirdek.gorevler().kuyrukla( "Belge Açılıyor", [yol, sonuc, hata, tamam, simgeler](GorevBaglam& b) { // WORKER: saf CPU (GPU/sahne yasak) b.ilerlet(0.05f, "Dosya okunuyor"); std::string veri; { std::ifstream f(yol, std::ios::binary | std::ios::ate); if (!f) { *hata = "Dosya açılamadı: " + yol; return; } const std::streamsize n = f.tellg(); if (n <= 0) { *hata = "Boş dosya: " + yol; return; } veri.resize((size_t)n); f.seekg(0); f.read(veri.data(), n); } if (b.iptalIstendi()) return; b.ilerlet(0.15f, "Çözülüyor"); io::FikoBelge belge; if (!io::fikoOku(veri, belge, *hata)) return; // *tamam=false → finalize hata gösterir *simgeler = std::move(belge.agacSimgeleri); // öğe simge override → finalize'da uygula // Ağır tessellate 0.15→0.95 (iptal düğüm bazlı kontrol edilir). if (!io::belgeCoz(belge, *sonuc, [&](f32 o, std::string_view m) { b.ilerlet(o, m); }, [&] { return b.iptalIstendi(); })) return; // iptal → finalize çağrılmaz (durum Iptal) *tamam = true; }, [&b, sonuc, hata, tamam, yol, simgeler]() { // FINALIZE: main-thread (GPU + sahne + kamera) if (!*tamam) { b.bilgiVer(hata->empty() ? "Belge açılamadı" : ("Belge açılamadı — " + *hata)); return; } std::unordered_map dugumHarita; // eski→yeni (malzeme atama remap'i) io::belgeUygula(b.sahne, b.kamera, *sonuc, &b.partD, &b.olcum, &b.pmiSis, &dugumHarita); // feature + ölçüm + PMI malzemeleriBelgedenAl(*sonuc, dugumHarita, b.malzemeKut, b.malzemeAta); // v1.16 MLZM // Kinematik mekanizma: uzuv→düğüm bağları YÜKLEMEDE değişen Varlik'lerle remap edilir. mekanizmaUygula(b.mekanizma, sonuc->uzuvlar, sonuc->mafsallar, sonuc->yasalar, dugumHarita); // v1.17 KNMT // CAE kurulumları: `dugum` yüklemede DEĞİŞEN Varlik'tir → mekanizma uzuvlarıyla // AYNI haritayla remap; eşleşmeyen kayıt DÜŞÜRÜLÜR (yanlış gövdeye analiz bağlamaktansa). b.caeKurulumlar.clear(); for (io::FikoCaeKurulum c : sonuc->caeKurulumlar) { const auto it = dugumHarita.find(c.dugum); if (it == dugumHarita.end()) continue; c.dugum = it->second; b.caeKurulumlar.push_back(c); } ui::AgacSimgeKayit::global().ogeleriTemizle(); // öğe-bazlı simge override'larını geri yükle for (const auto& [id, deger] : *simgeler) { ui::AgacSimgeKayit::global().ogeAyarla(id, deger); // render önbelleği if (auto* gvo = b.cekirdek.gorunum((Varlik)id)) gvo->agacSimgeOzelAyarla(deger); // objenin kalıtımsal UI özelliği (sahip) } // Kurtarma anlık görüntüsü geçici ağaçtan yüklenir: MRU'ya YAZMA (her an // budanabilir bir yol "son dosyalar"da durmamalı). if (b.sonDosyaEkle && !gecici::kokIcindeMi(yol)) b.sonDosyaEkle(yol); if (b.belgeAdiAyarla) b.belgeAdiAyarla(dosyaTabanAdi(yol)); // süreç yöneticisi belge adı otoKayit().belgeYolu(yol); // kurtarma metası + kirli sıfırla b.bilgiVer("Belge açıldı: " + yol); }); } void fikoAc(UygulamaBaglam& b) { fikoYol(b, fikoDosyaSec((HWND)b.pencere.dogalTutamac(), false)); } // .fikocad ASENKRON kaydet: snapshot (belgeCikar) ana-thread'te — govdeBaytYaz OCCT // serialize dahil, tutarlı/atomik görüntü — sonra worker serialize+sıkıştır+atomik yaz. void fikoKaydetKomut(UygulamaBaglam& b) { const std::string yol = fikoDosyaSec((HWND)b.pencere.dogalTutamac(), true); if (yol.empty()) return; fikoKaydetYola(b, yol); } // Diyalogsuz kaydetme ÇEKİRDEĞİ: yol dışarıdan gelir (makro/MCP otomasyonu, betik, // ileride "Kaydet" kısayolu). Gövde `fikoKaydetKomut`tan BİREBİR taşındı — davranış // aynı (aynı snapshot + aynı worker + aynı finalize/MRU/oto-kayıt bağı). void fikoKaydetYola(UygulamaBaglam& b, const std::string& yol) { if (yol.empty()) return; Cekirdek& cekirdek = b.cekirdek; if (dosyaIsiAktif(cekirdek, "Belge Kaydediliyor")) { b.bilgiVer("Kayıt sürüyor, lütfen bekleyin…"); return; } // SNAPSHOT ana-thread (kuyruklamadan ÖNCE): UCUZ görüntü — brepErtele=true ile ağır // BinTools serialize worker'a ertelenir (yalnız shared_ptr handle alınır → UI donmaz). // Handle değişmez (op'lar yeni handle üretir) → sahne eşzamanlı düzenlense de tutarlı. auto belge = std::make_shared( io::belgeCikar(b.sahne, b.kamera, &b.partD, /*brepErtele=*/true, &b.olcum, &b.pmiSis)); belge->agacSimgeleri = ui::AgacSimgeKayit::global().ogeListesi(); // öğe simge override (v1.5 ASMG) malzemeleriBelgeyeYaz(b.malzemeKut, b.malzemeAta, *belge); // malzeme + atamalar (v1.16 MLZM) mekanizmaCikar(b.mekanizma, *belge); // kinematik uzuv/mafsal/yasa (v1.17 KNMT) belge->caeKurulumlar = b.caeKurulumlar; // yapısal analiz kurulumları (v1.19 CAEK) auto hata = std::make_shared(); auto tamam = std::make_shared(false); cekirdek.gorevler().kuyrukla( "Belge Kaydediliyor", [belge, yol, hata, tamam](GorevBaglam& b) { // WORKER: saf CPU (serialize+POD+deflate+disk) // Ertelenmiş gövdeleri PARALEL serileştir (ana-thread donmaz), sonra POD+sıkıştır. if (!io::belgeBrepSerilestir(*belge, [&](f32 o, std::string_view m) { b.ilerlet(o, m); }, [&] { return b.iptalIstendi(); })) return; std::string veri; if (!io::fikoYaz(*belge, veri, *hata, [&](f32 o, std::string_view m) { b.ilerlet(o, m); })) return; if (b.iptalIstendi()) return; // iptal → hedef dosya dokunulmaz (rename yok) b.ilerlet(0.9f, "Yazılıyor"); if (!io::dosyayaYaz(yol, veri, *hata)) return; b.ilerlet(1.0f, "Yazıldı"); *tamam = true; }, [&b, yol, hata, tamam]() { // FINALIZE: main-thread (MRU + bilgi) if (!*tamam) { b.bilgiVer(hata->empty() ? "Belge kaydedilemedi" : ("Kaydedilemedi — " + *hata)); return; } if (b.sonDosyaEkle) b.sonDosyaEkle(yol); // başarılı kayıt → MRU if (b.belgeAdiAyarla) b.belgeAdiAyarla(dosyaTabanAdi(yol)); // süreç yöneticisi belge adı otoKayit().belgeYolu(yol); // kurtarma metası + kirli sıfırla b.bilgiVer("Belge kaydedildi: " + yol); }); } // Gövdeleri ASENKRON dışa aktar (STEP/STL/OBJ/IGES — uzantıya göre worker'da dispatch). // Ana-thread yalnız UCUZ snapshot (brep shared_ptr + dönüşüm; op'lar yeni handle üretir → // kopya tutarlı); donusumUygula (OCCT kopya+transform) + yazım worker'da. disaAktarKomut // ve stepKaydetKomut ORTAK kullanır (aynı donma her iki yolda da vardı). static void govdeleriAsenkronAktar(UygulamaBaglam& b, const std::string& s, std::string basariMesaj, std::string hataMesaj) { struct AktarGirdi { std::shared_ptr brep; Donusumd donusum; }; auto girdiler = std::make_shared>(); const bool yalnizSecili = !b.sahne.secililer().empty(); for (const auto& d : b.sahne.dugumler()) { if (yalnizSecili && !b.sahne.govdeSeciliMi(d.kimlik)) continue; if (!d.brep || !d.brep->gecerliMi()) continue; // Sağ tık ile verilen per-yüz renk d.yuzOz'da (Dugum override — görüntü/undo katmanı) // yaşar, brep.yuzRenkleri()'ne YAZILMAZ. Dışa aktarım brep'i doğrudan okuduğu için // buradaki override'ı export ÖNCESİ brep kopyasına "pişirmezsek" STEP renksiz çıkar // (bkz. brep_io.cpp stepKaydet — yalnız brep.yuzRenkleri() yazar). if (!d.yuzOz.empty()) { auto pisirilmis = std::make_shared(geometri::govdeDerinKopya(*d.brep)); const u32 n = pisirilmis->yuzSayisi(); std::vector renkler = pisirilmis->yuzRenkliMi() ? pisirilmis->yuzRenkleri() : std::vector(n, d.yuzVars.renk); renkler.resize(n, d.yuzVars.renk); for (const auto& oz_kv : d.yuzOz) { const u32 ord = oz_kv.first; if (ord >= 1 && ord <= n) renkler[ord - 1] = oz_kv.second.renk; } pisirilmis->yuzRenkleriAta(std::move(renkler)); girdiler->push_back({pisirilmis, d.donusum}); } else { girdiler->push_back({d.brep, d.donusum}); } } if (girdiler->empty()) { b.bilgiVer(std::move(hataMesaj) + " — gövde yok"); return; } auto tamam = std::make_shared>(false); b.cekirdek.gorevler().kuyrukla( "Dışa Aktarılıyor", [girdiler, s, tamam](GorevBaglam& gb) { // WORKER: saf OCCT (sahneye dokunmaz) std::vector dunya; dunya.reserve(girdiler->size()); size_t i = 0; for (const AktarGirdi& g : *girdiler) { if (gb.iptalIstendi()) return; dunya.push_back(geometri::donusumUygula(*g.brep, g.donusum)); gb.ilerlet(0.6f * (f32)(++i) / (f32)girdiler->size(), "Geometri hazırlanıyor"); } std::vector liste; for (const auto& g : dunya) liste.push_back(&g); gb.ilerlet(0.7f, "Yazılıyor"); std::string uz; if (const size_t n = s.find_last_of('.'); n != std::string::npos) { uz = s.substr(n + 1); for (char& c : uz) c = (char)std::tolower((unsigned char)c); } if (uz == "stl") *tamam = geometri::stlKaydet(liste, s); else if (uz == "obj") *tamam = geometri::objKaydet(liste, s); else if (uz == "iges" || uz == "igs") *tamam = geometri::igesKaydet(liste, s); else *tamam = geometri::stepKaydet(liste, s); }, [&b, s, tamam, basariMesaj = std::move(basariMesaj), hataMesaj = std::move(hataMesaj)]() { // FINALIZE: MRU + bilgi if (*tamam && b.sonDosyaEkle) b.sonDosyaEkle(s); b.bilgiVer(*tamam ? basariMesaj : hataMesaj); }); } // Çok-formatlı dışa aktarım: kaydet-dialog (STEP/STL/OBJ/IGES filtresi) → sahne.disaAktar // uzantıya göre doğru dışa aktarıcıyı çağırır. Uzantı yazılmazsa seçili filtreye göre eklenir. void disaAktarKomut(UygulamaBaglam& b) { lisansSaglikKontrol(); // kontrol noktası #3 wchar_t yol[MAX_PATH] = {}; OPENFILENAMEW ofn{}; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = (HWND)b.pencere.dogalTutamac(); ofn.lpstrFilter = L"STEP (*.step)\0*.step\0STL (*.stl)\0*.stl\0" L"Wavefront OBJ (*.obj)\0*.obj\0IGES (*.iges)\0*.iges\0"; ofn.lpstrFile = yol; ofn.nMaxFile = MAX_PATH; ofn.nFilterIndex = 1; ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST; if (!GetSaveFileNameW(&ofn)) return; std::wstring w(yol); if (w.find(L'.') == std::wstring::npos) { // uzantı yoksa filtreye göre ekle const wchar_t* ekler[] = {L".step", L".stl", L".obj", L".iges"}; const int i = (ofn.nFilterIndex >= 1 && ofn.nFilterIndex <= 4) ? (int)ofn.nFilterIndex - 1 : 0; w += ekler[i]; } const int bayt = WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, nullptr, 0, nullptr, nullptr); std::string s(bayt - 1, 0); WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, s.data(), bayt, nullptr, nullptr); govdeleriAsenkronAktar(b, s, "Dışa aktarıldı: " + s, "Dışa aktarılamadı"); } // ---- APT/CL (.FikoApt) içe aktarma: parse worker'da, yükleme main-thread'de. // STEP import kalıbı (kuyrukla + finalize) — CAM operasyonu + sahne yol düğümü. static void aptYol(UygulamaBaglam& b, const std::string& yol) { Cekirdek& cekirdek = b.cekirdek; if (yol.empty()) return; if (b.sonDosyaEkle) b.sonDosyaEkle(yol); // MRU kaydı (başarılı seçim → kalıcı) auto sonuc = std::make_shared(); cam::AptOkuSecenek aptSec; aptSec.yayToleransMm = aptYayTolerans(); // ana thread'de oku (worker'a POD kopya) cekirdek.gorevler().kuyrukla( "APT İçe Aktar", [yol, sonuc, aptSec](GorevBaglam& b) { // WORKER: saf CPU (getline parse) b.ilerlet(0.1f, "APT okunuyor"); *sonuc = cam::aptOku(yol, aptSec); b.ilerlet(1.0f, "Bitti"); }, [&b, sonuc]() { // FINALIZE: main-thread (CAM op + sahne düğümü + olay) if (!sonuc->basarili) { b.bilgiVer(sonuc->hata.empty() ? "APT okunamadı" : ("APT: " + sonuc->hata)); return; } const bool sim = aptIthalSimulasyon(); const u32 op = b.camSis.aptIceAktar(*sonuc, /*sahneyeCiz=*/!sim, aptAktiviteAyir()); if (!op) { b.bilgiVer("APT yolu boş / yüklenemedi"); return; } b.camSis.acik = true; // Kac operasyon (aktivite) acildi → kullanici dosyanin bolundugunu gorsun. const std::string opBilgi = sonuc->aktiviteler.size() > 1 ? (", " + std::to_string(sonuc->aktiviteler.size()) + " operasyon") : std::string(); if (sim) { // Sahneye çizmedik: veri tutuldu, yolu simülasyon penceresinde oynat. // Çok operasyonlu APT → 0 (tüm program): her aktivite timeline'da AYRI // segment olur. Tek op kimliğiyle açılsaydı yalnız ilki görünürdü. camSimPencereAc(sonuc->aktiviteler.size() > 1 ? 0u : op); // Kamerayı yola oturt: sim kipinde sahne düğümleri SONRAKİ karede kurulur, // yani tumunuSigdir burada yolu göremez. AABB'yi doğrudan hamlelerden al. // (Yoksa kamera eski yerinde kalıp yolun arkasında/içinde kalıyordu — APT // koordinatları makine orijininde, ör. X≈1120.) Aabbf ys; for (const cam::Hamle& h : sonuc->yol.hamleler) ys.genislet({(f32)h.nokta.x, (f32)h.nokta.y, (f32)h.nokta.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); } b.bilgiVer("APT içe aktarıldı → simülasyon (" + std::to_string(sonuc->gotoSayisi) + " GOTO" + opBilgi + ")"); } else { b.tumunuSigdir(); b.bilgiVer("APT içe aktarıldı (" + std::to_string(sonuc->gotoSayisi) + " GOTO" + opBilgi + ")"); } }); } void aptAc(UygulamaBaglam& b) { aptYol(b, aptDosyaSec((HWND)b.pencere.dogalTutamac(), false)); } // ---- .FikoOp (Fikoperasyon) içe aktarma: gzip+JSON parse + mesh + takım yolu // worker'da (fiko::io::fikoOpOku), sahneye bağlama main-thread'de (ithalSonucuKur). static void fikoOpYol(UygulamaBaglam& b, const std::string& yol) { Cekirdek& cekirdek = b.cekirdek; if (yol.empty()) return; if (b.sonDosyaEkle) b.sonDosyaEkle(yol); auto sonuc = std::make_shared(); cekirdek.gorevler().kuyrukla( "FikoOp İçe Aktar", [yol, sonuc](GorevBaglam& g) { // WORKER: gzip inflate + simdjson + mesh/yol *sonuc = io::fikoOpOku(yol, [&g](f32 o, std::string_view m) { g.ilerlet(o, m); }); }, [&b, sonuc]() { // FINALIZE: main-thread (grup + mesh düğüm + yol düğümü) if (!sonuc->basarili) { b.bilgiVer(sonuc->hata.empty() ? "FikoOp okunamadı" : ("FikoOp: " + sonuc->hata)); return; } const u32 grup = io::ithalSonucuKur(b.sahne, *sonuc, &b.camSis); if (!grup) { b.bilgiVer("FikoOp boş (mesh/yol yok)"); return; } // Program varsa CAM görünümü aç (web'deki gibi sim'de oynanabilir). if (!sonuc->programlar.empty()) b.camSis.acik = true; b.tumunuSigdir(); std::string msj = "FikoOp içe aktarıldı — " + std::to_string(sonuc->cizimler.size()) + " çizim, " + std::to_string(sonuc->programlar.size()) + " program (" + std::to_string(sonuc->toplamNokta) + " nokta)"; if (sonuc->atlananCizim) msj += ", " + std::to_string(sonuc->atlananCizim) + " sıkıştırılmış çizim atlandı"; b.bilgiVer(msj); }); } // ---- .wrl (VRML97) içe aktarma: STEP gibi BREP (per-yüz/kenar seçim + hover). // AĞIR iş (vrmlOku parse + BrepGovde + ucgenle BRepMesh + altKimlik remap) WORKER'da; // yalnız GPU yükleme (ithalEkleHazir) main-thread'de → dev WRL'de pencere DONMAZ. // stepYol kalıbı (Sahne::vrmlIceAktar'ın bö+ async hali; monolitik metot donduruyordu). static void wrlYol(UygulamaBaglam& b, const std::string& yol) { Cekirdek& cekirdek = b.cekirdek; if (yol.empty()) return; if (b.sonDosyaEkle) b.sonDosyaEkle(yol); struct WrlHazir { geometri::IthalGovde ig; // BREP gövde (yüz→Shape seçim + per-yüz renk) MeshVeri mesh; // ucgenle çıktısı (altKimlik = Shape ordinali) KenarVeri kenar; // feature kenarları (IndexedLineSet) bool basarili = false; }; auto sonuc = std::make_shared(); cekirdek.gorevler().kuyrukla( "VRML İçe Aktar", [yol, sonuc](GorevBaglam& g) { // WORKER: saf CPU (parse + BREP + mesh) g.ilerlet(0.1f, "VRML okunuyor"); KenarVeri kenarlar; std::vector yuzRenkleri; std::vector yuzShape; // her yüz → Shape (CATIA yüzü / seçim grubu) geometri::BrepGovde govde = geometri::vrmlOku(yol, &kenarlar, &yuzRenkleri, &yuzShape); if (!govde.gecerliMi()) return; if (!yuzRenkleri.empty()) govde.yuzRenkleriAta(std::move(yuzRenkleri)); g.ilerlet(0.5f, "Üçgenleniyor"); MeshVeri mesh = govde.ucgenle(Vec3f{0.62f, 0.66f, 0.72f}); // Seçim ordinali: üçgen-yüz → Shape (bir yüze tık = tüm CATIA yüzü, web `grup`). if (!yuzShape.empty()) { u32 maks = 0; for (auto& v : mesh.vertexler) if (v.altKimlik >= 1 && v.altKimlik <= yuzShape.size()) { v.altKimlik = yuzShape[v.altKimlik - 1]; if (v.altKimlik > maks) maks = v.altKimlik; } mesh.yuzSayi = maks; } // Akıllı seçim (benzerYuzler/paralelYuzler) yüz sınıflandırma cache'ini WORKER'da // pre-warm et → her seçimde 10ms+ main-thread taraması yerine <1ms cache okuma. govde.yuzBilgiOnbellekKur(); sonuc->ig.govde = std::move(govde); sonuc->mesh = std::move(mesh); sonuc->kenar = std::move(kenarlar); sonuc->basarili = true; g.ilerlet(1.0f, "Bitti"); }, [&b, sonuc]() { // FINALIZE: main-thread (GPU yükleme + sahne düğümü) if (!sonuc->basarili) { b.bilgiVer("VRML okunamadı / boş (geçerli gövde yok)"); return; } b.sahne.ithalEkleHazir(std::move(sonuc->ig), std::move(sonuc->mesh), std::move(sonuc->kenar), {}, VARLIK_YOK); b.tumunuSigdir(); b.bilgiVer("VRML içe aktarıldı (BREP — yüz/kenar seçilebilir, STEP gibi)"); }); } void webAc(UygulamaBaglam& b) { yolAc(b, webDosyaSec((HWND)b.pencere.dogalTutamac())); } // Toolbar/menü BİRLEŞİK "Aç": tumDosyaSec dialogunda kullanıcı seçim yaparsa // (webAc/stepAc/aptAc/fikoAc kalıbı) merkezi yolAc'a devreder — uzantı bazlı // yönlendirme (lisans kapısı dahil) TEK yerde yaşamaya devam eder. void acKomut(UygulamaBaglam& b) { yolAc(b, tumDosyaSec((HWND)b.pencere.dogalTutamac())); } // "Yeni Belge" = yeni fikocad.exe SÜREÇİ (süreç izolasyonu → kendi aygıtı/belgesi/ // penceresi, paylaşımlı-GPU çok-pencere karmaşası yok). Süreç başlatıcı; CreateProcessW // main.cpp'deki komut gövdesiyle birebir aynı. void yeniBelgeKomut(UygulamaBaglam& b) { wchar_t exeYol[MAX_PATH]{}; GetModuleFileNameW(nullptr, exeYol, MAX_PATH); STARTUPINFOW si{}; si.cb = sizeof(si); PROCESS_INFORMATION pi{}; if (CreateProcessW(exeYol, nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } } // Yola göre içe aktar: uzantıya göre STEP veya APT içe aktarıcıyı çağırır. // Universal palet "Dosya" kaynağı buradan hızlı yeniden açar (dialog yok). void yolAc(UygulamaBaglam& b, const std::string& yol) { if (yol.empty()) return; // TEK merkezi lisans kapısı — menü/sürükle-bırak/palet/oto-kurtarma/iş takip paneli // dahil BURADAN geçmeyen bir dosya açma yolu artık yok (bkz. lisans_yukle.hpp). if (!surukleBirakIzinli(yol)) { if (b.bilgiVer) b.bilgiVer("Bu dosya türünü açma hakkı lisansınıza dahil değil"); return; } std::string uz; if (const size_t d = yol.find_last_of('.'); d != std::string::npos) for (char c : yol.substr(d)) uz += (char)std::tolower((unsigned char)c); if (uz == ".fikocad") { fikoYol(b, yol); // yerel belge (tam durum + kamera) } else if (uz == ".apt" || uz == ".fikoapt") { aptYol(b, yol); } else if (uz == ".step" || uz == ".stp") { stepYol(b, yol); // STEP içe aktarıcı (STEPCAFControl_Reader) } else if (uz == ".fikoop") { fikoOpYol(b, yol); // Fikoperasyon (mesh + takım yolu, web export) } else if (uz == ".wrl") { wrlYol(b, yol); // VRML97 mesh (CATIA export) } else { // Desteklenmeyen uzantı (ör. sürükle-bırak ile gelen rastgele dosya): çökme YOK. if (b.bilgiVer) b.bilgiVer("Desteklenmeyen dosya türü — .fikocad, STEP (.step/.stp), " ".FikoApt, .FikoOp ve .wrl açılabilir"); } } // ---- APT/CL dışa aktarma: seçili operasyon → .apt (ncKaydetSec kalıbı). void aptAktar(UygulamaBaglam& b) { const int kimlik = b.camSis.seciliOp ? b.camSis.seciliOp : b.camSis.seciliKimlik; if (kimlik <= 0) { b.bilgiVer("Önce üretim ağacından bir operasyon seçin"); return; } const std::string yol = aptDosyaSec((HWND)b.pencere.dogalTutamac(), true); if (yol.empty()) return; // Metin ANA-THREAD'de üretilir (CAM verisi yalnız main'de okunur — worker'dan // okumak kullanıcı op silerken race olurdu), DISK yazımı worker'da (ağ sürücüsünde // donma kalkar). AptYazildi olayı yazım başarılı olunca finalize'da (davranış birebir). auto metin = std::make_shared(); u32 hamleSayi = 0; if (!b.camSis.aptMetinUret((u32)kimlik, *metin, hamleSayi)) { b.bilgiVer("APT aktarılamadı (yol hesaplı mı?)"); return; } auto tamam = std::make_shared>(false); b.cekirdek.gorevler().kuyrukla( "APT Dışa Aktar", [metin, yol, tamam](GorevBaglam&) { // WORKER: yalnız disk std::ofstream f(yol, std::ios::binary); if (f) { f.write(metin->data(), (std::streamsize)metin->size()); *tamam = (bool)f; } }, [&b, tamam, kimlik, hamleSayi]() { if (*tamam) b.cekirdek.olaylar().yayinla(AptYazildi{(u32)kimlik, hamleSayi}, "APT"); b.bilgiVer(*tamam ? "APT dışa aktarıldı (.apt)" : "APT aktarılamadı (disk yazılamadı)"); }); } // ---- G-Kod (NC) dışa aktarma: tüm program → .nc/.tap (eski Üretim Programı // panelindeki ⭳ G-Kod düğmesinin yerini alır; artık CAM menüsünden). void gkodAktar(UygulamaBaglam& b) { lisansSaglikKontrol(); // kontrol noktası #4 (CAM çıktısı — en değerli üretim adımı) wchar_t yolW[MAX_PATH] = {}; OPENFILENAMEW ofn{}; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = (HWND)b.pencere.dogalTutamac(); ofn.lpstrFilter = L"NC Programı (*.nc;*.tap)\0*.nc;*.tap\0Tümü (*.*)\0*.*\0"; ofn.lpstrFile = yolW; ofn.nMaxFile = MAX_PATH; ofn.lpstrDefExt = L"nc"; ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST; if (!GetSaveFileNameW(&ofn)) return; const int bayt = WideCharToMultiByte(CP_UTF8, 0, yolW, -1, nullptr, 0, nullptr, nullptr); std::string yol(bayt - 1, 0); WideCharToMultiByte(CP_UTF8, 0, yolW, -1, yol.data(), bayt, nullptr, nullptr); // Metin main'de (CAM verisi), disk worker'da (disaAktar/apt kalıbı). auto metin = std::make_shared(b.camSis.gkod()); auto tamam = std::make_shared>(false); b.cekirdek.gorevler().kuyrukla( "G-Kod Yazılıyor", [metin, yol, tamam](GorevBaglam&) { std::ofstream f(yol, std::ios::binary); if (f) { f.write(metin->data(), (std::streamsize)metin->size()); *tamam = (bool)f; } }, [&b, tamam]() { b.bilgiVer(*tamam ? "G-Kod yazıldı (.nc)" : "G-Kod yazılamadı"); }); } } // namespace fiko