#include "kinematik_penceresi.hpp" #include #include #include #include #include "imgui.h" #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/cekirdek/gorev.hpp" #include "fiko/geometri/brep.hpp" #include "fiko/kinematik/mekanizma.hpp" #include "fiko/cae/govde_kafes.hpp" #include "fiko/io/fiko_format.hpp" #include "fiko/dokum/voxel_gorsel.hpp" #include "fiko/dokum/voxel_kafes.hpp" #include "fiko/montaj/mekanizma_sahne.hpp" #include "fiko/sahne/sahne.hpp" #include "fiko/ui/web_skin.hpp" namespace fiko { namespace { struct KinDurum { f64 zaman = 0.0; f64 zamanSon = 1.0; // kaydırıcı üst sınırı (yasa periyodu genelde 1 sn) int taramaAdim = 24; // DMU örnek sayısı bool kesin = true; // B-Rep doğrulaması (yanlış-pozitif eler) bool isaretle = true; // girişim hacmini sahnede göster std::string sonRapor; std::vector isaretler; // panelin koyduğu girişim gövdeleri }; KinDurum& kinDurum() { static KinDurum d; return d; } // Panel kendi mafsallarını "sürülen" saymaz: elle sürüş Lua tarafının kavramı. // Kapalı zincir çözücüsü yasası olan mafsalları serbest bırakmasın diye yasalı // mafsallar sürülen kabul edilir (Lua yolundaki `surulenKume` ile aynı ilke). std::vector yasaliMafsallar(const kinematik::Mekanizma& mek) { std::vector s; for (const kinematik::MafsalYasa& y : mek.yasalar()) s.push_back(y.mafsal); return s; } // ---- FEM (yapısal analiz) panel durumu ---- struct FemDurum { int cozunurluk = 24; f64 elastisite = 210000.0; // MPa (çelik) f64 poisson = 0.3; int sabitYuz = 0; // KafesYuz sırası // Kısıt kipi: false = TAM ankastre (3 eksen), true = SİMETRİ (yalnız yüzün normali). // Tam ankastre Poisson daralmasını engelleyip modeli YAPAY sertleştirir — ölçüldü // 07-21: eksenel uzamada %-0.80 sapma, simetri kipinde %0.00. bool simetri = false; int yukYuz = 1; f64 fx = 0.0, fy = 0.0, fz = -1000.0; // N (toplam) std::string rapor; u32 haritaDugum = 0; // gerilme haritası düğümü (varsa) bool kosuyor = false; // çözüm arka planda sürüyor (buton kilitli) }; FemDurum& femDurum() { static FemDurum d; return d; } const char* const kYuzAd[6] = {"-X", "+X", "-Y", "+Y", "-Z", "+Z"}; void isaretleriTemizle(Sahne& sahne, KinDurum& d) { for (u32 k : d.isaretler) if (sahne.bul(k)) sahne.sil(k); d.isaretler.clear(); } // Seçili (ya da tek) gövdeyi voxelleyip lineer elastik FEM çözer; von Mises // alanını mevcut `dokum::alanMeshKur` ile renkli küp haritasına çevirir. // Motor tarafına yeni yol EKLEMEZ: cae::govdedenKafes + cae::femCoz + alanMeshKur. // Belgedeki kurulumu panele yükle / panelden belgeye yaz (gövde başına kalıcı). void femKurulumOku(std::vector& kayitlar, u32 dugum, FemDurum& f) { for (const io::FikoCaeKurulum& c : kayitlar) if (c.dugum == dugum) { f.cozunurluk = (int)c.cozunurluk; f.elastisite = c.elastisite; f.poisson = c.poisson; f.sabitYuz = c.sabitYuz; f.yukYuz = c.yukYuz; f.fx = c.kuvvet[0]; f.fy = c.kuvvet[1]; f.fz = c.kuvvet[2]; f.simetri = c.simetriKisit != 0; return; } } void femKurulumYaz(std::vector& kayitlar, u32 dugum, const FemDurum& f) { io::FikoCaeKurulum c; c.dugum = dugum; c.cozunurluk = (u32)f.cozunurluk; c.elastisite = f.elastisite; c.poisson = f.poisson; c.sabitYuz = (u8)f.sabitYuz; c.yukYuz = (u8)f.yukYuz; c.simetriKisit = f.simetri ? 1 : 0; c.konservatif = cae::konservatifKafes() ? 1 : 0; c.kuvvet[0] = f.fx; c.kuvvet[1] = f.fy; c.kuvvet[2] = f.fz; for (io::FikoCaeKurulum& v : kayitlar) if (v.dugum == dugum) { v = c; return; } kayitlar.push_back(c); } void femBolumuCiz(Cekirdek& cekirdek, Sahne& sahne, std::vector& kayitlar) { FemDurum& f = femDurum(); if (!ImGui::CollapsingHeader("Yapısal Analiz (FEM)")) return; const std::vector secili = sahne.seciliGovdeler(); const u32 hedef = secili.empty() ? 0u : secili[0]; if (!hedef) { ImGui::TextWrapped("Analiz edilecek gövdeyi sahnede ya da ağaçta seçin."); return; } const Dugum* hd = sahne.bul(hedef); ImGui::Text("Gövde: %s", hd ? hd->ad.c_str() : "?"); // Gövde DEĞİŞTİYSE o gövdenin BELGEDE KAYITLI kurulumunu yükle (v1.19 CAEK). static u32 sonHedef = 0; if (hedef != sonHedef) { sonHedef = hedef; femKurulumOku(kayitlar, hedef, f); } ImGui::SetNextItemWidth(110.0f); ImGui::InputInt("Çözünürlük", &f.cozunurluk); if (f.cozunurluk < 4) f.cozunurluk = 4; ImGui::SetNextItemWidth(110.0f); ImGui::InputDouble("E (MPa)", &f.elastisite); ImGui::SameLine(); ImGui::SetNextItemWidth(90.0f); ImGui::InputDouble("ν", &f.poisson); ImGui::SetNextItemWidth(90.0f); ImGui::Combo("Sabit yüz", &f.sabitYuz, kYuzAd, 6); ImGui::SameLine(); ImGui::SetNextItemWidth(90.0f); ImGui::Combo("Yük yüzü", &f.yukYuz, kYuzAd, 6); ImGui::Checkbox("Simetri kısıtı (yalnız normal)", &f.simetri); if (ImGui::IsItemHovered()) ImGui::SetTooltip( "KAPALI: sabit yüz 3 eksende ankastre — cıvatalı/kaynaklı bağlantı gibi, ama " "Poisson daralmasını engelleyip modeli bir miktar SERTLEŞTİRİR.\n" "AÇIK: her yüz yalnız kendi normalinde tutulur (simetri düzlemi) — parça " "serbestçe daralır; analitik kıyaslarda sapma sıfıra iner."); f64 kuvvet[3] = {f.fx, f.fy, f.fz}; ImGui::SetNextItemWidth(240.0f); if (ImGui::InputScalarN("Kuvvet (N)", ImGuiDataType_Double, kuvvet, 3)) { f.fx = kuvvet[0]; f.fy = kuvvet[1]; f.fz = kuvvet[2]; } ImGui::BeginDisabled(f.kosuyor); const bool cozIstendi = ImGui::Button("Çöz"); ImGui::EndDisabled(); if (f.kosuyor) { ImGui::SameLine(); ImGui::TextDisabled("çözülüyor…"); } if (cozIstendi && hd && hd->brep && hd->brep->gecerliMi()) { femKurulumYaz(kayitlar, hedef, f); // kurulum BELGEYE yazılır (kaydet/aç korur) if (f.haritaDugum && sahne.bul(f.haritaDugum)) sahne.sil(f.haritaDugum); f.haritaDugum = 0; f.rapor.clear(); f.kosuyor = true; // AĞIR KISIM WORKER'DA: voxelleme + CG çözümü saf CPU'dur (Vulkan/Sahne YOK). // Ölçüldü 07-21: çözünürlük 60'ta çözüm ~700 ms → ana thread'de yapılırsa pencere // o süre boyunca DONAR (kampanyanın ana şikayeti). Sahneye dokunan kısım (mesh + // düğüm) `bitince` içinde MAIN-thread'de koşar. struct FemIs { cae::HexKafes kf; dokum::VoxelKafes vk; cae::FemSonuc sonuc; std::string rapor; Donusumd don; }; auto is = std::make_shared(); is->don = hd->donusum; auto brep = hd->brep; // shared_ptr: worker'da güvenle yaşar const cae::KafesKurulum kur{(u32)f.cozunurluk, 0.01, cae::konservatifKafes()}; const cae::ElastikMalzeme mlz{f.elastisite, f.poisson}; const cae::KafesYuz sYuz = (cae::KafesYuz)f.sabitYuz; const cae::KafesYuz yYuz = (cae::KafesYuz)f.yukYuz; const bool simetri = f.simetri; const Vec3d kuvvet{f.fx, f.fy, f.fz}; FemDurum* fp = &f; Sahne* sp = &sahne; cekirdek.gorevler().kuyrukla( "FEM çözümü", [is, brep, kur, mlz, sYuz, yYuz, simetri, kuvvet](GorevBaglam& g) { g.ilerlet(0.1f, "kafes kuruluyor"); is->kf = cae::govdedenKafes(*brep, kur, &is->vk); if (!is->kf.gecerliMi()) { is->rapor = "Kafes kurulamadı (geçersiz gövde ya da çok kaba çözünürlük)."; return; } std::vector kisitlar; if (simetri) { kisitlar = cae::yuzKisitlari(is->kf, sYuz, cae::yuzNormalMaske(sYuz)); for (const cae::KafesYuz y : {cae::KafesYuz::EksiY, cae::KafesYuz::EksiZ}) { if (cae::yuzNormalMaske(y) == cae::yuzNormalMaske(sYuz)) continue; const auto ek = cae::yuzKisitlari(is->kf, y, cae::yuzNormalMaske(y)); kisitlar.insert(kisitlar.end(), ek.begin(), ek.end()); } } else { for (u32 dg : cae::yuzDugumleri(is->kf, sYuz)) kisitlar.push_back({dg, 0x7}); } g.ilerlet(0.4f, "çözülüyor"); is->sonuc = cae::femCoz(is->kf, mlz, kisitlar, cae::yuzeYayliKuvvet(is->kf, yYuz, kuvvet), [&g] { return g.iptalIstendi(); }); const u32 incede = cae::enIncedeHucre(is->kf); char b[420]; std::snprintf(b, sizeof b, "%s kafes %ux%ux%u (h=%.2f) adım=%d artık=%.1e\n" "en büyük yer değiştirme = %.4g mm\n" "en büyük von Mises = %.4g MPa (ANKASTRE KÖKTE yığılma)%s", is->sonuc.yakinsadi ? "Yakınsadı." : "YAKINSAMADI — güvenme!", is->kf.nx, is->kf.ny, is->kf.nz, is->kf.h, is->sonuc.adim, is->sonuc.artik, is->sonuc.enBuyukYer, is->sonuc.enBuyukVonMises, (incede && incede < cae::asgariCidarHucre()) ? "\nUYARI: KABA KAFES — en ince yönde çok az hücre, güvenme" : ""); is->rapor = b; }, [is, fp, sp] { // MAIN-thread: sahne + mesh (Vulkan burada güvenli) fp->kosuyor = false; fp->rapor = is->rapor; if (!is->sonuc.yakinsadi || !is->vk.gecerliMi() || is->sonuc.vonMises.size() != is->vk.hucreSayi()) return; std::vector alan(is->sonuc.vonMises.size()); for (std::size_t i = 0; i < alan.size(); ++i) alan[i] = (f32)is->sonuc.vonMises[i]; dokum::VoxelGorselAyar ga; ga.alan = dokum::VoxelAlan::Sicaklik; // alanMeshKur yalnız rampa/aralık kullanır ga.rampa = dokum::VoxelRampa::Turbo; ga.otoMinMax = true; ga.kirpParcaya = true; dokum::VoxelGorselSonuc vg = dokum::alanMeshKur(is->vk, alan, ga); if (!vg.gecerli) return; char lj[128]; std::snprintf(lj, sizeof lj, "\nharita renk skalası: %.4g … %.4g MPa (mavi→kırmızı, %u hücre)", (f64)vg.alanMin, (f64)vg.alanMax, vg.hucreSayi); fp->rapor += lj; const u32 dg = sp->hazirMeshEkle("FEM Gerilme Haritası", VARLIK_YOK, {0.6f, 0.6f, 0.6f}, std::move(vg.mesh)); if (Dugum* nd = sp->bul(dg)) nd->donusum = is->don; fp->haritaDugum = dg; }); } ImGui::SameLine(); if (ImGui::Button("Haritayı Kaldır")) { if (f.haritaDugum && sahne.bul(f.haritaDugum)) sahne.sil(f.haritaDugum); f.haritaDugum = 0; } if (!f.rapor.empty()) ImGui::TextUnformatted(f.rapor.c_str()); } } // namespace bool& kinematikPaneliAcik() { static bool a = false; return a; } void kinematikPaneliCiz(Cekirdek& cekirdek, Sahne& sahne, kinematik::Mekanizma& mek, std::vector& caeKurulumlar) { if (!kinematikPaneliAcik()) return; KinDurum& d = kinDurum(); ui::webSkinKromBasla(); bool acik = kinematikPaneliAcik(); if (!ImGui::Begin("⚙ Kinematik / DMU", &acik)) { ImGui::End(); ui::webSkinKromBitir(); kinematikPaneliAcik() = acik; return; } ui::webSkinGovdeBasla(0.0f); const auto& uzuvlar = mek.uzuvlar(); const auto& mafsallar = mek.mafsallar(); ImGui::Text("Uzuv: %d Mafsal: %d DOF: %d", (int)uzuvlar.size(), (int)mafsallar.size(), (int)mek.serbestlikDerecesi()); ImGui::SameLine(); ImGui::TextDisabled(mek.agacMi() ? "(ağaç)" : "(kapalı zincir)"); if (uzuvlar.size() <= 1 && mafsallar.empty()) { ImGui::TextWrapped( "Bu belgede mekanizma yok. Uzuv/mafsal şu an makro (Lua) ya da MCP ile kurulur: " "fiko.kinematik.sifirla / uzuvEkle / sabitle / oner / onerEkle."); femBolumuCiz(cekirdek, sahne, caeKurulumlar); // FEM mekanizmadan BAĞIMSIZ ui::webSkinGovdeBitir(); ImGui::End(); ui::webSkinKromBitir(); kinematikPaneliAcik() = acik; return; } // ---- Zaman: yasaları t anına uygula + sahneye yaz ---- ImGui::Separator(); static const f64 sifir = 0.0; bool degisti = ImGui::SliderScalar("Zaman (sn)", ImGuiDataType_Double, &d.zaman, &sifir, &d.zamanSon, "%.3f"); ImGui::SetNextItemWidth(120.0f); ImGui::InputScalar("Bitiş (sn)", ImGuiDataType_Double, &d.zamanSon); if (d.zamanSon <= 0.0) d.zamanSon = 1.0; if (degisti) { mek.zamanaAyarla(d.zaman); montaj::mekanizmaSahneyeYaz(sahne, cekirdek, mek, yasaliMafsallar(mek)); } // ---- Mafsalları elle sür ---- if (ImGui::CollapsingHeader("Mafsallar")) { for (std::size_t i = 0; i < mafsallar.size(); ++i) { kinematik::Mafsal* mfp = mek.mafsal(i); if (!mfp || mfp->tip == kinematik::MafsalTip::Sabit) continue; kinematik::Mafsal& mf = *mfp; char etiket[128]; std::snprintf(etiket, sizeof etiket, "%s##%d", mf.ad.empty() ? kinematik::mafsalTipAdi(mf.tip) : mf.ad.c_str(), (int)i); ImGui::SetNextItemWidth(180.0f); if (ImGui::DragScalar(etiket, ImGuiDataType_Double, &mf.q, 0.01f)) montaj::mekanizmaSahneyeYaz(sahne, cekirdek, mek, yasaliMafsallar(mek)); } } // ---- DMU girişim taraması ---- ImGui::Separator(); ImGui::SetNextItemWidth(100.0f); ImGui::InputInt("Örnek", &d.taramaAdim); if (d.taramaAdim < 1) d.taramaAdim = 1; ImGui::Checkbox("Kesin (B-Rep)", &d.kesin); ImGui::SameLine(); ImGui::Checkbox("Sahnede işaretle", &d.isaretle); if (ImGui::Button("Çarpışma Tara")) { isaretleriTemizle(sahne, d); const std::vector kutular = montaj::uzuvYerelKutulari(sahne, mek); auto liste = kinematik::carpismaTara(mek, kutular, 0.0, d.zamanSon, d.taramaAdim, true); int gercek = 0; std::string rapor; for (const kinematik::Carpisma& c : liste) { mek.zamanaAyarla(c.zaman); if (kinematik::halkaOtoCoz() && !mek.halkaKapatanlar().empty()) mek.kapaliZincirCoz(yasaliMafsallar(mek)); const auto dunya = mek.ileriKinematik(); const Dugum* da = c.uzuvA < uzuvlar.size() ? sahne.bul(uzuvlar[c.uzuvA].sahne) : nullptr; const Dugum* db = c.uzuvB < uzuvlar.size() ? sahne.bul(uzuvlar[c.uzuvB].sahne) : nullptr; f64 hacim = -1.0; if (d.kesin) { if (!da || !db || !da->brep || !db->brep) continue; const geometri::BrepGovde ga = geometri::donusumUygula(*da->brep, dunya[c.uzuvA]); const geometri::BrepGovde gb = geometri::donusumUygula(*db->brep, dunya[c.uzuvB]); geometri::BrepGovde ortak = geometri::kesisim(ga, gb); hacim = ortak.gecerliMi() ? ortak.hacim() : 0.0; if (hacim <= kinematik::girisimAsgariHacim()) continue; // teğet temas → ele if (d.isaretle) { char ad[192]; std::snprintf(ad, sizeof ad, "Girişim: %s × %s (t=%.3f)", uzuvlar[c.uzuvA].ad.c_str(), uzuvlar[c.uzuvB].ad.c_str(), c.zaman); const u32 yeni = sahne.gsdDugumEkle(DugumTip::Yuzey, ad, VARLIK_YOK, kinematik::girisimRenk()); if (yeni && sahne.gsdDugumGuncelle( yeni, std::make_shared(std::move(ortak)))) { if (Dugum* yd = sahne.bul(yeni)) yd->saydamlik = kinematik::girisimAlfa(); d.isaretler.push_back(yeni); } else if (yeni) { sahne.sil(yeni); } } } ++gercek; char satir[256]; std::snprintf(satir, sizeof satir, "%s × %s t=%.3f %s\n", uzuvlar[c.uzuvA].ad.c_str(), uzuvlar[c.uzuvB].ad.c_str(), c.zaman, hacim >= 0.0 ? ("hacim " + std::to_string(hacim) + " mm³").c_str() : "(AABB)"); rapor += satir; } // Tarama mafsal değişkenlerini değiştirdi → paneldeki ana geri kur. mek.zamanaAyarla(d.zaman); montaj::mekanizmaSahneyeYaz(sahne, cekirdek, mek, yasaliMafsallar(mek)); d.sonRapor = gercek ? rapor : std::string("Girişim bulunamadı."); } ImGui::SameLine(); if (ImGui::Button("İşaretleri Temizle")) { isaretleriTemizle(sahne, d); d.sonRapor.clear(); } if (!d.sonRapor.empty()) ImGui::TextUnformatted(d.sonRapor.c_str()); femBolumuCiz(cekirdek, sahne, caeKurulumlar); ui::webSkinGovdeBitir(); ImGui::End(); ui::webSkinKromBitir(); kinematikPaneliAcik() = acik; } } // namespace fiko