#include "strafor_penceresi.hpp" #include #include #include #include #include #include #include #include "imgui.h" #include "ayar_kayitlari.hpp" #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/cekirdek/dunya.hpp" #include "fiko/core/kare_istek.hpp" #include "fiko/sahne/sahne.hpp" #include "fiko/ui/pencere_cerceve.hpp" // ui::Pencere — merkezi pencere iskeleti #include "fiko/strafor/strafor.hpp" namespace fiko { namespace { struct GeciciGirdi { char isim[48] = "Artık"; double en = 600, boy = 600, yuk = 100; int adet = 1; }; struct StraforDurum { bool baslatildi = false; strafor::HedefParca hedef; std::vector gecici; // artık havuzu bool geciciOncelik = false; bool offcutNesting = true; // giyotin artık yeniden kullanım f64 iscilikAgirlik = 2.0; // sıralama işçilik önceliği std::vector secenekler; int seciliIdx = 0; GeciciGirdi yeniGecici; // ---- Sezon (her sezon = kendi Body'si; ürettiği HER ŞEY o body altında) ---- u32 sezonBody = 0; // "Strafor Kesici.N" üst body (lazy, ilk Diz'de) u32 montajGrup = 0; // body altında "Montaj" grubu std::vector uretilen; // bu sezonun kalıcı sahne düğümleri (Diz'de topluca silinir) // ---- Animasyon (montaj + kesim) ---- strafor::MontajPlani dizPlan; // en son dizilen plan (animasyon kaynağı) std::vector dizNode; // dizPlan.bloklar ile hizalı sahne düğüm kimlikleri Vec3d montajMerkez{}; // dizilen montajın dünya merkezi (patlama yönü için) bool futuristik = true; // her bloğa spektrum rengi int animKip = 0; // 0=yok, 1=montaj, 2=kesim std::vector animSira; // işlenecek blok indeksleri (sıralı) int animI = 0; // aktif adım f64 animT = 0; // 0..1 aktif blok ilerlemesi f64 animHiz = 1.0; // hız çarpanı f64 animSaat = 0; // efekt nabzı için biriken zaman u32 telNode = 0; // hareketli sıcak-tel düğümü u32 artikGrup = 0; // "Kesim Artıkları" grubu // Sönümlenen ışıltı efektleri (yapıştırma flash'ı): salt transform + saydamlık. struct Efekt { u32 node = 0; f64 omur = 0; f64 omurBas = 1; }; std::vector efektler; }; bool g_acik = false; StraforDurum g_d; // AKTİF sezonun canlı durumu // Çok-sezon: her body bir sezon. Aktif olan g_d'de; ötekiler kayıtta (çift-tıkla dön). u32 g_aktifSezon = 0; // aktif sezon body kimliği (0 = henüz Diz'lenmemiş) int g_sezonSayac = 0; // "Strafor Kesici.N" numaralayıcı std::set g_sezonBodyler; // tüm sezon body kimlikleri std::map g_sezonKayit; // aktif olmayan sezonların anlık kopyası void baslatVars() { if (g_d.baslatildi) return; g_d.hedef = {straforVarsayilanHedefEn(), straforVarsayilanHedefBoy(), straforVarsayilanHedefYuk()}; g_d.geciciOncelik = straforGeciciOncelik(); g_d.offcutNesting = straforOffcutNesting(); g_d.iscilikAgirlik = straforIscilikAgirlik(); g_d.baslatildi = true; } void hesapla() { g_d.secenekler = strafor::montajHesapla(g_d.hedef, strafor::standartKutukler(), g_d.gecici, g_d.geciciOncelik, (int)straforVaryantSayisi(), g_d.offcutNesting, g_d.iscilikAgirlik); g_d.seciliIdx = 0; } Vec3f hsv2rgb(f32 h, f32 s, f32 v) { const f32 i = std::floor(h * 6.0f); const f32 f = h * 6.0f - i; const f32 p = v * (1 - s), q = v * (1 - f * s), t = v * (1 - (1 - f) * s); switch (((int)i) % 6) { case 0: return {v, t, p}; case 1: return {q, v, p}; case 2: return {p, v, t}; case 3: return {p, q, v}; case 4: return {t, p, v}; default: return {v, p, q}; } } // Futuristik spektrum: grid konumundan türeyen benzersiz ton (katman = doygunluk kayması). Vec3f futuristikRenk(const strafor::BlokYerlestirme& b, int toplam) { const f32 lineer = toplam > 0 ? (f32)(b.gridPoz.iy * 7 + b.gridPoz.ix * 3 + b.gridPoz.iz) : 0.0f; const f32 ton = std::fmod(0.55f + lineer * 0.083f, 1.0f); // cyan'dan başlar const f32 doygun = 0.62f + 0.06f * (b.gridPoz.iz % 3); return hsv2rgb(ton, doygun, 0.98f); } Vec3f blokRengi(const strafor::BlokYerlestirme& b, int toplam) { // Futuristik: spektrum ton. Gerçekçi: KAYNAK kütük kalınlığının rengi → ince kenar // (50mm) kütüğü büyük 300mm bloklardan görsel olarak AYRIŞIR. if (g_d.futuristik) return futuristikRenk(b, toplam); return strafor::kalinlikRengi(b.kaynakKalinlik > 0 ? b.kaynakKalinlik : b.gercekBoyut.yukseklik); } // Düğümü kimlikten bul → Sahne::bul (ECS sparse-set O(1)). Her-kare animasyonda büyük // CAD modeli yüklüyken eski lineer tarama binlerce alakasız düğümü gezerdi. Dönen Dugum* // dense depoya işaret eder → ekle/sil sonrası saklanmaz (çağıran her kare taze çözer). Dugum* dugumBul(Sahne& sahne, u32 kimlik) { if (!kimlik) return nullptr; return sahne.bul(kimlik); } f64 easeInOutCubic(f64 t) { return t < 0.5 ? 4 * t * t * t : 1 - std::pow(-2 * t + 2, 3) / 2; } // Bir blok düğümünü kesim ekseninde verilen boya ayarla (SALT transform → remesh yok). // Geometri gercekBoyut'ta kurulu; olcek = dim/gercek. Kesilmeyen (kalan) kenar sabit: // min kenar = pozisyon → merkez = pozisyon + dim/2. void blokBoyAyarla(Dugum& d, const strafor::BlokYerlestirme& b, bool eksenX, f64 dim) { const f64 gEn = b.gercekBoyut.en, gBoy = b.gercekBoyut.boy, gYuk = b.gercekBoyut.yukseklik; Vec3d olcek{1, 1, 1}; Vec3d merkez{b.pozisyon.x + gEn * 0.5, b.pozisyon.y + gBoy * 0.5, b.pozisyon.z + gYuk * 0.5}; if (eksenX) { olcek.x = dim / gEn; merkez.x = b.pozisyon.x + dim * 0.5; } else { olcek.y = dim / gBoy; merkez.y = b.pozisyon.y + dim * 0.5; } d.donusum.olcek = olcek; d.donusum.konum = merkez; } void animasyonDurdur(Sahne& sahne); // ileri bildirim (Diz öncesi transient temizliği) // Bir düğümü sezon body'sinin altındaki gruba bağla (kök sahneye taşmasın). void sezoneBagla(Sahne& sahne, u32 node, u32 grup) { if (node && grup) sahne.dunya().ekle(node, HiyerarsiBilesen{grup}); } // Aktif sezonun ürettiği tüm kalıcı düğümleri sil (Body KALIR) → tekrar Diz eklemesin. void sezonTemizle(Sahne& sahne) { for (auto it = g_d.uretilen.rbegin(); it != g_d.uretilen.rend(); ++it) sahne.sil(*it); g_d.uretilen.clear(); g_d.dizNode.clear(); g_d.montajGrup = 0; g_d.artikGrup = 0; } // Aktif sezonun üst Body'sini al (yoksa oluştur: "Strafor Kesici.N", body altında yaşar). u32 sezonBodyAl(Cekirdek& cekirdek, Sahne& sahne) { if (g_d.sezonBody && cekirdek.nesne(g_d.sezonBody)) return g_d.sezonBody; char ad[48]; std::snprintf(ad, sizeof(ad), "Strafor Kesici.%d", ++g_sezonSayac); const u32 body = (u32)sahne.yeniBody(ad); g_d.sezonBody = body; g_aktifSezon = body; g_sezonBodyler.insert(body); return body; } // Planı sahnede gerçek OCCT kutu düğümleri olarak diz — HEPSİ sezon body'si altında, // "Montaj" alt grubunda. Önceki diz silinir (tekrar eklemez). void sahneyeDiz(Cekirdek& cekirdek, Sahne& sahne, const strafor::MontajPlani& plan) { animasyonDurdur(sahne); // süren animasyon/tel/efekt temizlensin sezonTemizle(sahne); // önceki montaj+artık düğümleri gitsin (birikme yok) const u32 body = sezonBodyAl(cekirdek, sahne); char grupAd[64]; std::snprintf(grupAd, sizeof(grupAd), "Montaj %dx%d %s", plan.adetX, plan.adetY, plan.kalinlikAciklama.c_str()); const u32 montaj = (u32)sahne.grupEkle(grupAd, body); g_d.montajGrup = montaj; g_d.uretilen.push_back(montaj); g_d.dizNode.clear(); g_d.dizNode.reserve(plan.bloklar.size()); for (const auto& b : plan.bloklar) { Dugum& d = sahne.ekle(DugumTip::Kutu); d.p1 = (f32)b.gercekBoyut.en; d.p2 = (f32)b.gercekBoyut.boy; d.p3 = (f32)b.gercekBoyut.yukseklik; d.renk = blokRengi(b, plan.toplamBlok); char ad[80]; std::snprintf(ad, sizeof(ad), "%s [%d,%d,%d]%s", b.id.c_str(), b.gridPoz.ix, b.gridPoz.iy, b.gridPoz.iz, b.geciciKutukId.empty() ? "" : " (artık)"); d.ad = ad; sahne.yenidenInsa(d); // brep+mesh yeni ölçüyle d.donusum.konum = {b.pozisyon.x + b.gercekBoyut.en * 0.5, b.pozisyon.y + b.gercekBoyut.boy * 0.5, b.pozisyon.z + b.gercekBoyut.yukseklik * 0.5}; sezoneBagla(sahne, d.kimlik, montaj); g_d.dizNode.push_back(d.kimlik); g_d.uretilen.push_back(d.kimlik); } g_d.dizPlan = plan; f64 toplamYuk = 0; for (const auto& k : plan.katmanlar) toplamYuk += k.kalinlik; g_d.montajMerkez = {plan.hedef.en * 0.5, plan.hedef.boy * 0.5, toplamYuk * 0.5}; (void)cekirdek; kareIste(); } // Bir blokta kesim ekseni X mi? (kesimMiktari.en>0 → X, aksi Y). Sadece kesilecek bloklar. bool kesimEkseniX(const strafor::BlokYerlestirme& b) { return b.kesimMiktari.en > 0; } Vec3f karis(const Vec3f& a, const Vec3f& b, f32 t) { return {a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t}; } Vec3d blokMerkez(const strafor::BlokYerlestirme& b) { return {b.pozisyon.x + b.gercekBoyut.en * 0.5, b.pozisyon.y + b.gercekBoyut.boy * 0.5, b.pozisyon.z + b.gercekBoyut.yukseklik * 0.5}; } // Montaj patlama başlangıcı: son konumdan merkeze göre dışa + yukarı savrulmuş nokta. Vec3d montajBaslangic(const strafor::BlokYerlestirme& b) { const Vec3d m = blokMerkez(b); Vec3d yon{m.x - g_d.montajMerkez.x, m.y - g_d.montajMerkez.y, 0}; const f64 boy = std::sqrt(yon.x * yon.x + yon.y * yon.y); if (boy > 1e-3) { yon.x /= boy; yon.y /= boy; } else { yon = {1, 0, 0}; } const f64 yaricap = std::max(g_d.dizPlan.hedef.en, g_d.dizPlan.hedef.boy) * 0.7 + 400; return {m.x + yon.x * yaricap, m.y + yon.y * yaricap, m.z + 900 + (b.gridPoz.iz * 120.0)}; } // Bir bloğu diz-son haline sabitle (olcek 1, gerçek merkez, opak) — durdur/bitiş finalize. void blokFinalize(Dugum& d, const strafor::BlokYerlestirme& b) { d.donusum.olcek = {1, 1, 1}; d.donusum.konum = blokMerkez(b); d.saydamlik = 1.0f; } // Sönümlenen ışıltı efekti (yapıştırma flash'ı): ince parlak kutu, saydamlıkla söner. void efektEkle(Sahne& sahne, Vec3d konum, Vec3d olcek, Vec3f renk, f64 omur) { Dugum& e = sahne.ekle(DugumTip::Kutu); e.p1 = e.p2 = e.p3 = 1.0f; e.renk = renk; e.ad = "Yapıştırma"; e.saydamlik = 0.9f; sahne.yenidenInsa(e); e.donusum.olcek = olcek; e.donusum.konum = konum; sezoneBagla(sahne, e.kimlik, g_d.sezonBody); // efekt de body içinde kalsın g_d.efektler.push_back({e.kimlik, omur, omur}); } // Bloğun yapıştırma yüzlerinde parlak cyan flash kur (komşu temas anı). void yapistirmaFlashlari(Sahne& sahne, const strafor::BlokYerlestirme& b) { const f64 gEn = b.gercekBoyut.en, gBoy = b.gercekBoyut.boy, gYuk = b.gercekBoyut.yukseklik; const Vec3f renk{0.25f, 1.0f, 1.0f}; for (const auto& y : b.yapistamaYuzleri) { Vec3d konum, olcek; if (y.eksen == strafor::Eksen::X) { konum = {b.pozisyon.x, b.pozisyon.y + gBoy * 0.5, b.pozisyon.z + gYuk * 0.5}; olcek = {4, gBoy, gYuk}; } else if (y.eksen == strafor::Eksen::Y) { konum = {b.pozisyon.x + gEn * 0.5, b.pozisyon.y, b.pozisyon.z + gYuk * 0.5}; olcek = {gEn, 4, gYuk}; } else { konum = {b.pozisyon.x + gEn * 0.5, b.pozisyon.y + gBoy * 0.5, b.pozisyon.z}; olcek = {gEn, gBoy, 4}; } efektEkle(sahne, konum, olcek, renk, 0.6); } } void efektGuncelle(Sahne& sahne, f64 dt) { for (size_t i = 0; i < g_d.efektler.size();) { auto& ef = g_d.efektler[i]; ef.omur -= dt; if (ef.omur <= 0) { sahne.sil(ef.node); g_d.efektler.erase(g_d.efektler.begin() + i); continue; } if (Dugum* d = dugumBul(sahne, ef.node)) d->saydamlik = (f32)(ef.omur / ef.omurBas * 0.9); ++i; } } void efektleriTemizle(Sahne& sahne) { for (auto& ef : g_d.efektler) sahne.sil(ef.node); g_d.efektler.clear(); } void animasyonDurdur(Sahne& sahne) { // Kalan blokları diz-son haline sabitle (yarım kalan uçuş/kesim toparlanır). for (size_t i = 0; i < g_d.dizNode.size() && i < g_d.dizPlan.bloklar.size(); ++i) if (Dugum* d = dugumBul(sahne, g_d.dizNode[i])) blokFinalize(*d, g_d.dizPlan.bloklar[i]); if (g_d.telNode) { sahne.sil(g_d.telNode); g_d.telNode = 0; } efektleriTemizle(sahne); g_d.animKip = 0; kareIste(); } bool dizGecerli() { return !g_d.dizNode.empty() && g_d.dizNode.size() == g_d.dizPlan.bloklar.size(); } // ── Montaj animasyonu: bloklar dağınık+saydam başlar → sıra sıra yerine uçar ── void montajBaslat(Sahne& sahne) { if (!dizGecerli()) return; animasyonDurdur(sahne); g_d.animSira.clear(); for (int i = 0; i < (int)g_d.dizPlan.bloklar.size(); ++i) g_d.animSira.push_back(i); // Alttan-yukarı, içten-dışa dalga: katman (iz) sonra merkeze uzaklık. std::sort(g_d.animSira.begin(), g_d.animSira.end(), [](int a, int c) { const auto& ba = g_d.dizPlan.bloklar[a]; const auto& bc = g_d.dizPlan.bloklar[c]; if (ba.gridPoz.iz != bc.gridPoz.iz) return ba.gridPoz.iz < bc.gridPoz.iz; const Vec3d ma = blokMerkez(ba), mc = blokMerkez(bc); const f64 da = std::hypot(ma.x - g_d.montajMerkez.x, ma.y - g_d.montajMerkez.y); const f64 dc = std::hypot(mc.x - g_d.montajMerkez.x, mc.y - g_d.montajMerkez.y); return da < dc; }); // Tüm blokları dağınık başlangıca + yarı saydama al. for (int idx : g_d.animSira) if (Dugum* d = dugumBul(sahne, g_d.dizNode[idx])) { d->donusum.olcek = {1, 1, 1}; d->donusum.konum = montajBaslangic(g_d.dizPlan.bloklar[idx]); d->saydamlik = 0.15f; } g_d.animKip = 1; g_d.animI = 0; g_d.animT = 0; kareIste(); } void montajGuncelle(Sahne& sahne, f64 dt) { if (g_d.animI >= (int)g_d.animSira.size()) { animasyonDurdur(sahne); return; } const f64 SURE = 0.35; g_d.animT += (dt * g_d.animHiz) / SURE; const f64 te = easeInOutCubic(std::clamp(g_d.animT, 0.0, 1.0)); const int idx = g_d.animSira[g_d.animI]; const auto& b = g_d.dizPlan.bloklar[idx]; const Vec3d bas = montajBaslangic(b), son = blokMerkez(b); if (Dugum* d = dugumBul(sahne, g_d.dizNode[idx])) { d->donusum.konum = {bas.x + (son.x - bas.x) * te, bas.y + (son.y - bas.y) * te, bas.z + (son.z - bas.z) * te}; d->saydamlik = (f32)(0.15 + 0.85 * te); } if (g_d.animT >= 1.0) { if (Dugum* d = dugumBul(sahne, g_d.dizNode[idx])) blokFinalize(*d, b); yapistirmaFlashlari(sahne, b); // temas → yapıştırma ışıltısı g_d.animI++; g_d.animT = 0; if (g_d.animI >= (int)g_d.animSira.size()) { g_d.animKip = 0; } } } // ── Kesim animasyonu: kesilecek bloklar tam→trim küçülür, nabızlı tel süpürür ── void kesimBaslat(Sahne& sahne) { if (!dizGecerli()) return; animasyonDurdur(sahne); g_d.animSira.clear(); for (int i = 0; i < (int)g_d.dizPlan.bloklar.size(); ++i) { const auto& b = g_d.dizPlan.bloklar[i]; if (b.kesilecekMi && (b.kesimMiktari.en > 0 || b.kesimMiktari.boy > 0)) g_d.animSira.push_back(i); } if (g_d.animSira.empty()) return; for (int idx : g_d.animSira) if (Dugum* d = dugumBul(sahne, g_d.dizNode[idx])) { const auto& b = g_d.dizPlan.bloklar[idx]; const bool ex = kesimEkseniX(b); blokBoyAyarla(*d, b, ex, ex ? b.tamBoyut.en : b.tamBoyut.boy); } Dugum& tel = sahne.ekle(DugumTip::Kutu); tel.p1 = tel.p2 = tel.p3 = 1.0f; tel.renk = {0.1f, 0.9f, 1.0f}; tel.ad = "Sıcak Tel"; sahne.yenidenInsa(tel); g_d.telNode = tel.kimlik; sezoneBagla(sahne, tel.kimlik, g_d.sezonBody); // tel de body içinde g_d.artikGrup = (u32)sahne.grupEkle("Kesim Artıkları", g_d.sezonBody); g_d.uretilen.push_back(g_d.artikGrup); g_d.animKip = 2; g_d.animI = 0; g_d.animT = 0; } void kesimGuncelle(Cekirdek& cekirdek, Sahne& sahne, f64 dt) { (void)cekirdek; // artık body-parenting sahne.dunya() üzerinden if (g_d.animI >= (int)g_d.animSira.size()) { animasyonDurdur(sahne); return; } const f64 SURE = 0.8; g_d.animT += (dt * g_d.animHiz) / SURE; const f64 te = easeInOutCubic(std::clamp(g_d.animT, 0.0, 1.0)); const int idx = g_d.animSira[g_d.animI]; const auto& b = g_d.dizPlan.bloklar[idx]; const bool ex = kesimEkseniX(b); const f64 tam = ex ? b.tamBoyut.en : b.tamBoyut.boy; const f64 hedef = ex ? b.gercekBoyut.en : b.gercekBoyut.boy; const f64 simdi = tam + (hedef - tam) * te; if (Dugum* d = dugumBul(sahne, g_d.dizNode[idx])) blokBoyAyarla(*d, b, ex, simdi); if (Dugum* tel = dugumBul(sahne, g_d.telNode)) { const f64 gBoy = b.gercekBoyut.boy, gEn = b.gercekBoyut.en, gYuk = b.gercekBoyut.yukseklik; const f64 kenar = ex ? b.pozisyon.x + simdi : b.pozisyon.y + simdi; tel->donusum.olcek = ex ? Vec3d{10, gBoy, gYuk} : Vec3d{gEn, 10, gYuk}; tel->donusum.konum = {ex ? kenar : b.pozisyon.x + gEn * 0.5, ex ? b.pozisyon.y + gBoy * 0.5 : kenar, b.pozisyon.z + gYuk * 0.5}; // Nabız: cyan ↔ magenta (akkor tel enerji hissi). const f32 nabiz = 0.5f + 0.5f * std::sin((f32)g_d.animSaat * 6.0f); tel->renk = karis({0.1f, 0.9f, 1.0f}, {1.0f, 0.2f, 0.9f}, nabiz); } if (g_d.animT >= 1.0) { if (Dugum* d = dugumBul(sahne, g_d.dizNode[idx])) blokBoyAyarla(*d, b, ex, hedef); const f64 kesim = ex ? b.kesimMiktari.en : b.kesimMiktari.boy; if (kesim > 0.5) { // artık = yeniden kullanılabilir stok (amber katı) Dugum& a = sahne.ekle(DugumTip::Kutu); a.p1 = (f32)(ex ? kesim : b.gercekBoyut.en); a.p2 = (f32)(ex ? b.gercekBoyut.boy : kesim); a.p3 = (f32)b.gercekBoyut.yukseklik; a.renk = {1.0f, 0.72f, 0.20f}; a.ad = "Artık " + b.id; sahne.yenidenInsa(a); a.donusum.konum = {ex ? b.pozisyon.x + b.gercekBoyut.en + kesim * 0.5 : b.pozisyon.x + b.gercekBoyut.en * 0.5, ex ? b.pozisyon.y + b.gercekBoyut.boy * 0.5 : b.pozisyon.y + b.gercekBoyut.boy + kesim * 0.5, b.pozisyon.z + b.gercekBoyut.yukseklik * 0.5}; sezoneBagla(sahne, a.kimlik, g_d.artikGrup); g_d.uretilen.push_back(a.kimlik); } g_d.animI++; g_d.animT = 0; if (g_d.animI >= (int)g_d.animSira.size()) animasyonDurdur(sahne); } } void animasyonGuncelle(Cekirdek& cekirdek, Sahne& sahne, f64 dt) { if (g_d.animKip == 0 && g_d.efektler.empty()) return; g_d.animSaat += dt * g_d.animHiz; efektGuncelle(sahne, dt * g_d.animHiz); if (g_d.animKip == 1) montajGuncelle(sahne, dt); else if (g_d.animKip == 2) kesimGuncelle(cekirdek, sahne, dt); kareIste(); } // Aktif sezonu kaydet (aktif olmayan sezonlar kayıtta bekler). void sezonKaydet() { if (g_aktifSezon) g_sezonKayit[g_aktifSezon] = g_d; } // Verilen sezon body'sini aktif yap (mevcut aktifi kaydedip onunkini yükler). void sezonAktifYap(u32 body) { if (body == g_aktifSezon) return; sezonKaydet(); if (auto it = g_sezonKayit.find(body); it != g_sezonKayit.end()) { g_d = it->second; g_sezonKayit.erase(it); } g_aktifSezon = body; } // Yeni sezon: aktifi kaydet, taze durum (body sonraki Diz'de oluşur). void yeniSezonBaslat() { sezonKaydet(); g_d = StraforDurum{}; g_aktifSezon = 0; } } // namespace void straforPencereAc() { g_acik = true; } bool straforSezonMu(u32 body) { return g_sezonBodyler.count(body) != 0; } void straforSezonAc(u32 body) { if (!g_sezonBodyler.count(body)) return; sezonAktifYap(body); g_acik = true; kareIste(); } void straforPencereCiz(Cekirdek& cekirdek, Sahne& sahne) { const bool animCanli = g_d.animKip != 0 || !g_d.efektler.empty(); if (!g_acik) { if (animCanli) animasyonDurdur(sahne); return; } baslatVars(); // Animasyon (montaj/kesim) + sönümlenen efektler kare-başı ilerler. if (animCanli) animasyonGuncelle(cekirdek, sahne, (f64)ImGui::GetIO().DeltaTime); // ui::Pencere iskeleti (pencere standardı): pin + yükseklik cap + global skin + // konum kalıcılığı merkezi (RAII — erken dönüşte End otomatik). ui::Pencere pen{"Strafor Kesici", &g_acik, {.boyut = {520, 560}}}; if (!pen) return; // ---- Sezon başlığı: aktif body + yeni sezon (her sezon kendi body'sinde) ---- { std::string sezonAd = "(yeni — Diz'de body oluşur)"; if (g_aktifSezon) { DokumanNesnesi* n = cekirdek.nesne(g_aktifSezon); sezonAd = n ? n->ad() : "Strafor Kesici"; } ImGui::TextColored(ImVec4(0.6f, 0.85f, 1.0f, 1.0f), "Sezon: %s", sezonAd.c_str()); ImGui::SameLine(); if (ImGui::SmallButton("➕ Yeni Sezon")) yeniSezonBaslat(); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Yeni body (Strafor Kesici.N) — ayrı sezon. Ağaçta body'ye çift tık = " "o sezonun düzenleme penceresi."); } ImGui::Separator(); ImGui::TextUnformatted("Hedef parça (mm)"); ImGui::PushItemWidth(120); ImGui::InputDouble("En", &g_d.hedef.en, 0, 0, "%.0f"); ImGui::SameLine(); ImGui::InputDouble("Boy", &g_d.hedef.boy, 0, 0, "%.0f"); ImGui::SameLine(); ImGui::InputDouble("Yükseklik", &g_d.hedef.yukseklik, 0, 0, "%.0f"); ImGui::PopItemWidth(); ImGui::Checkbox("Geçici (artık) kütük öncelikli", &g_d.geciciOncelik); ImGui::Checkbox("Artık yeniden kullan (nesting ◆)", &g_d.offcutNesting); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Bir kütükten kesilen artığı aynı planda sonraki parçalarda kullanır\n" "(daha az fire; karşılığında daha çok yapıştırma). Grid planları da kalır."); float isc = (float)g_d.iscilikAgirlik; ImGui::SetNextItemWidth(200); if (ImGui::SliderFloat("Öncelik", &isc, 0.0f, 6.0f, isc < 0.5f ? "malzeme (az fire)" : "işçilik (az yapıştırma)")) g_d.iscilikAgirlik = isc; if (ImGui::IsItemHovered()) ImGui::SetTooltip("Sıralama önceliği. Sağ → yapıştırıcıyı en az uğraştıran (az blok, grid) " "plan öne. Sol (0) → en az fire/malzeme (nesting öne çıkabilir)."); if (ImGui::Button("Hesapla")) hesapla(); ImGui::SameLine(); ImGui::TextDisabled("%d seçenek", (int)g_d.secenekler.size()); // ---- Geçici (artık) kütük havuzu ---- if (ImGui::CollapsingHeader("Artık kütükler")) { for (size_t i = 0; i < g_d.gecici.size();) { const auto& g = g_d.gecici[i]; ImGui::Text("%s %.0f×%.0f×%.0f ×%d", g.isim.c_str(), g.en, g.boy, g.yukseklik, g.adet); ImGui::SameLine(); ImGui::PushID((int)i); const bool sil = ImGui::SmallButton("Sil"); ImGui::PopID(); if (sil) g_d.gecici.erase(g_d.gecici.begin() + i); else ++i; } GeciciGirdi& y = g_d.yeniGecici; ImGui::PushItemWidth(90); ImGui::InputText("İsim", y.isim, sizeof(y.isim)); ImGui::InputDouble("gEn", &y.en, 0, 0, "%.0f"); ImGui::SameLine(); ImGui::InputDouble("gBoy", &y.boy, 0, 0, "%.0f"); ImGui::SameLine(); ImGui::InputDouble("gYuk", &y.yuk, 0, 0, "%.0f"); ImGui::InputInt("Adet", &y.adet); ImGui::PopItemWidth(); if (ImGui::Button("Artık Ekle") && y.en > 0 && y.boy > 0 && y.yuk > 0 && y.adet > 0) { char id[32]; std::snprintf(id, sizeof(id), "art-%d", (int)g_d.gecici.size() + 1); g_d.gecici.push_back({id, y.isim, y.en, y.boy, y.yuk, y.adet, strafor::kalinlikRengi(y.yuk), 0, true}); } } ImGui::Separator(); // ---- Seçenek tablosu (kolon başlığına tık = sırala, Ctrl+tık = çoklu sıralama) ---- if (!g_d.secenekler.empty()) { enum { KOL_NO = 0, KOL_GRID, KOL_KALINLIK, KOL_BLOK, KOL_FIRE, KOL_MALIYET, KOL_ARTIK }; const ImGuiTableFlags bayrak = ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Sortable | ImGuiTableFlags_SortMulti; if (ImGui::BeginTable("secenekler", 7, bayrak, ImVec2(0, 200))) { ImGui::TableSetupColumn("#", ImGuiTableColumnFlags_NoSort, 0, KOL_NO); ImGui::TableSetupColumn("Grid", 0, 0, KOL_GRID); ImGui::TableSetupColumn("Kalınlık", 0, 0, KOL_KALINLIK); ImGui::TableSetupColumn("Blok", ImGuiTableColumnFlags_DefaultSort, 0, KOL_BLOK); ImGui::TableSetupColumn("Fire%", 0, 0, KOL_FIRE); ImGui::TableSetupColumn("Maliyet", 0, 0, KOL_MALIYET); ImGui::TableSetupColumn("Artık", 0, 0, KOL_ARTIK); ImGui::TableHeadersRow(); // Kolon sıralaması (çok-anahtarlı; başlığa tık = sırala, Ctrl+tık = ikincil ekle). // Her kare uygula (idempotent, ≤varyantLimit eleman) → başlık↔veri tutarlı + // Hesapla sonrası korunur; seçimi yalnız sıralama DEĞİŞİNCE başa al. if (ImGuiTableSortSpecs* ss = ImGui::TableGetSortSpecs(); ss && ss->SpecsCount > 0) { auto deger = [](const strafor::MontajPlani& p, ImGuiID kol) -> f64 { switch (kol) { case KOL_GRID: return (f64)(p.adetX * p.adetY); case KOL_KALINLIK: return (f64)p.katmanlar.size(); case KOL_BLOK: return (f64)p.toplamBlok; case KOL_FIRE: return p.fireOrani; case KOL_MALIYET: return p.toplamMaliyet; case KOL_ARTIK: return (f64)p.kullanilanGeciciAdet; default: return 0; } }; std::stable_sort(g_d.secenekler.begin(), g_d.secenekler.end(), [&](const strafor::MontajPlani& a, const strafor::MontajPlani& b) { for (int k = 0; k < ss->SpecsCount; ++k) { const auto& sp = ss->Specs[k]; const f64 va = deger(a, sp.ColumnUserID), vb = deger(b, sp.ColumnUserID); if (va != vb) return sp.SortDirection == ImGuiSortDirection_Ascending ? va < vb : va > vb; } return false; }); if (ss->SpecsDirty) { g_d.seciliIdx = 0; ss->SpecsDirty = false; } } for (int i = 0; i < (int)g_d.secenekler.size(); ++i) { const auto& p = g_d.secenekler[i]; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); char etik[16]; std::snprintf(etik, sizeof(etik), "%d", i + 1); if (ImGui::Selectable(etik, g_d.seciliIdx == i, ImGuiSelectableFlags_SpanAllColumns)) g_d.seciliIdx = i; ImGui::TableSetColumnIndex(1); ImGui::Text("%s%d×%d%s", p.nesting ? "◆ " : "", p.adetX, p.adetY, p.dondurulmus ? " ↻" : ""); ImGui::TableSetColumnIndex(2); ImGui::TextUnformatted(p.kalinlikAciklama.c_str()); ImGui::TableSetColumnIndex(3); ImGui::Text("%d", p.toplamBlok); ImGui::TableSetColumnIndex(4); ImGui::Text("%.1f", p.fireOrani); ImGui::TableSetColumnIndex(5); ImGui::Text("%.0f", p.toplamMaliyet); ImGui::TableSetColumnIndex(6); ImGui::Text("%d", p.kullanilanGeciciAdet); } ImGui::EndTable(); } // ---- Seçili plan özeti + diz ---- if (g_d.seciliIdx >= 0 && g_d.seciliIdx < (int)g_d.secenekler.size()) { const auto& p = g_d.secenekler[g_d.seciliIdx]; ImGui::Separator(); if (p.nesting) ImGui::TextColored(ImVec4(0.4f, 0.9f, 1.0f, 1.0f), "◆ Nesting planı — artık yeniden kullanımlı (az fire, çok yapıştırma)"); ImGui::Text("Katman: %s Blok: %d (%d tam, %d kesim)", p.kalinlikAciklama.c_str(), p.toplamBlok, p.tamBlokSayisi, p.toplamKesimSayisi); ImGui::Text("Fire: %.1f%% Maliyet: %.0f Yapıştırma: %.0f mm²", p.fireOrani, p.toplamMaliyet, p.toplamYapistamaAlani); // Kütük dökümü (BOM): her blok GERÇEKTE hangi kütükten? (ince kenar farklı kalınlık olabilir) { std::map dokum; for (const auto& b : p.bloklar) dokum[(i64)(b.kaynakKalinlik > 0 ? b.kaynakKalinlik : b.gercekBoyut.yukseklik)]++; std::string s = "Kütükler: "; bool ilk = true; for (auto it = dokum.rbegin(); it != dokum.rend(); ++it) { char parca[32]; std::snprintf(parca, sizeof(parca), "%s%lldmm ×%d", ilk ? "" : ", ", (long long)it->first, it->second); s += parca; ilk = false; } ImGui::TextUnformatted(s.c_str()); } if (p.kullanilanGeciciAdet > 0) ImGui::Text("Artık kullanımı: %d blok (tasarruf %.0f)", p.kullanilanGeciciAdet, p.geciciTasarruf); if (ImGui::Checkbox("Futuristik renkler", &g_d.futuristik) && !g_d.dizNode.empty()) { // Canlı yeniden renklendir (salt renk → remesh yok). for (size_t i = 0; i < g_d.dizNode.size() && i < g_d.dizPlan.bloklar.size(); ++i) if (Dugum* d = dugumBul(sahne, g_d.dizNode[i])) d->renk = blokRengi(g_d.dizPlan.bloklar[i], g_d.dizPlan.toplamBlok); kareIste(); } if (ImGui::Button("Sahneye Diz")) { animasyonDurdur(sahne); sahneyeDiz(cekirdek, sahne, p); } // ---- Animasyon: montaj (uçuş+yapıştırma) / kesim (nabızlı tel) ---- const bool dizildi = !g_d.dizNode.empty(); const bool calisiyor = g_d.animKip != 0; ImGui::SameLine(); ImGui::BeginDisabled(!dizildi || calisiyor); if (ImGui::Button("▶ Montaj")) montajBaslat(sahne); ImGui::SameLine(); if (ImGui::Button("▶ Kesim")) kesimBaslat(sahne); ImGui::EndDisabled(); ImGui::SameLine(); ImGui::BeginDisabled(!calisiyor); if (ImGui::Button("■ Durdur")) animasyonDurdur(sahne); ImGui::EndDisabled(); float hiz = (float)g_d.animHiz; ImGui::SetNextItemWidth(160); if (ImGui::SliderFloat("Hız", &hiz, 0.25f, 4.0f, "%.2f×")) g_d.animHiz = hiz; if (g_d.animKip == 1) ImGui::Text("Montaj blok %d / %d", g_d.animI + 1, (int)g_d.animSira.size()); else if (g_d.animKip == 2) ImGui::Text("Kesim %d / %d", g_d.animI + 1, (int)g_d.animSira.size()); } } else { ImGui::TextDisabled("Hedef girip \"Hesapla\" deyin."); } } } // namespace fiko