#pragma once // GSD Yüzey Offset gizmosu (Doğrudan Modelleme — Dalga C): tek bir GSD Yüzey düğümü // seçiliyken yüzey normali boyunca bir OFFSET oku belirir. Tut-sürükle → canlı paralel // yüzey önizlemesi (ucuz wireframe, OCCT yok); bırakınca PARAMETRİK "yuzeyOffset" GSD // feature'ı eklenir (ağaçta düzenlenebilir; pencere yolu ile birebir aynı sonuç). // push_pull.hpp kalıbının sade GSD ikizi — davranış ekler, HİÇBİR eski yolu değiştirmez. // AyarSistemi bayrağı dogrudanAyar().gsdGizmoAcik ile kapatılabilir (kapalı → eski davranış). #include #include #include #include #include #include #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/geometri/brep.hpp" #include "fiko/gsd/gsd_sistemi.hpp" #include "fiko/render/orbit_kamera.hpp" #include "fiko/sahne/sahne.hpp" #include "fiko/ui/bilesen_ek.hpp" #include "dm_tutamac_olay.hpp" #include "dogrudan_ayar.hpp" #include "gizmo_gorsel.hpp" #include "imgui.h" namespace fiko { struct GsdGizmoDurum { bool surukluyor = false; bool tutamacYakin = false; // imleç okta (dokunmatik; tuştan bağımsız) bool tel = false; // hedef GSD Tel mi (extrude) yoksa Yüzey mi (offset/uzat) bool kenarMod = false; // tek KENAR seçili → o kenarı uzat (yüzeyin tamamı değil) bool aciSurukle = false; // kenar dönme halkası sürükleniyor (aim açısı değişiyor) bool kenarKaydirMod = false; // kenarMod'da: false=Uzat (strip ekle), true=Kaydır (yüzeyi reshape) int mod = 0; // Yüzey (tüm) modu: 0=offset 1=uzat (çiplerle; KLAVYESİZ). Kalınlaştır SAĞ MENÜDE. f32 kenarAci = 1.5708f; // kenar aim açısı (rad): 0=düz(düzlemde) π/2=dik(normal); serbest döndür u32 dugum = 0; // sürüklenen GSD düğümü u32 kenarOrd = 0; // kenarMod'da uzatılan kenar ordinali f32 mesafe = 0; // canlı sürükleme mesafesi (mm; işaretli) Vec3f merkezD{}, normalD{}; // dünya: merkez + yön (sürükleme başında) char tipBuf[24] = ""; // sürüklerken yazılan hassas sayı (drag override) // Şişir modu çok-noktalı ızgara (Plasticity): yüzeye atılan pick edilebilir noktalar. u32 gridDugum = 0; // ızgaranın ait olduğu düğüm (değişince yeniden örneklenir) std::vector gridDunya; // dünya-uzayı ızgara noktaları (şişirme hedef adayları) int gridN = 0; // ızgara kenar boyu (nb): gridDunya.size() == nb*nb (canlı önizleme) std::vector sisirIdx; // seçili şişirme noktaları (grid index'leri; boş = merkez) std::vector sisirMik; // sisirIdx ile paralel: nokta başına SAKLI miktar (mm; editör modu) int sisirAktif = -1; // aktif nokta (sisirIdx POZİSYONU) — sürükleme bunun miktarını değiştirir }; // Yüzey (tüm) modu meta: 0=Ofset (yeşil), 1=Uzat (amber), 2=Şişir (cyan), 3=Kalınlaştır (pembe→KATI). inline const char* gsdModEtiket(int m) { return m == 1 ? "Uzat" : m == 2 ? "Şişir" : m == 3 ? "Kalınlaştır" : "Ofset"; } inline ImU32 gsdModRenk(int m) { return m == 1 ? IM_COL32(230, 200, 120, 255) : m == 2 ? IM_COL32(120, 220, 235, 255) : m == 3 ? IM_COL32(235, 160, 205, 255) // kalınlaştır → katı üretir : IM_COL32(180, 235, 150, 255); } // Seçili düğüm aynı-tip feature'ın SONUCU ise o feature'ı döndür (0 = yok). gsdTekFeature // ayarı: yeni feature yığmak yerine MEVCUDU güncelle (üst üste yüzey birikmesin). inline u32 gsdAyniTipFeature(gsd::GsdSistemi& gsdSis, u32 dugum, const char* tip) { if (!dogrudanAyar().gsdTekFeature || dugum == 0) return 0; for (u32 fk : gsdSis.featureler()) if (gsd::GsdFeature* f = gsdSis.bul(fk); f && f->sonucDugum() == dugum && f->tip() == tip) return fk; return 0; } // KAYNAĞI bu düğüm olan aynı-tip feature (TÜRETEN op'lar: kenar/tel extrude, kalınlaştır — // sonuç AYRI düğüm, kaynak görünür kalır → tekrar sürükleme aynı feature'ı güncellesin). inline u32 gsdKaynakliFeature(gsd::GsdSistemi& gsdSis, u32 kaynak, const char* tip) { if (!dogrudanAyar().gsdTekFeature || kaynak == 0) return 0; for (u32 fk : gsdSis.featureler()) if (gsd::GsdFeature* f = gsdSis.bul(fk); f && f->kaynak() == kaynak && f->tip() == tip) return fk; return 0; } // Yeni GSD sonucu üretilince/güncellenince SEÇİMİ sonuç düğümüne taşı: bir sonraki // sürükleme MEVCUT feature'ı güncellesin (gsdTekFeature). Seçim eski (gizlenen) kaynakta // kalınca her sürükleme YENİ yüzey türetiyordu (Fikri: "bir sürü yüzey oluşuyor"). inline void gsdSonucuSec(gsd::GsdSistemi& gsdSis, Sahne& sahne, u32 fk) { gsd::GsdFeature* f = fk ? gsdSis.bul(fk) : nullptr; if (!f || !f->sonucDugum() || !sahne.bul(f->sonucDugum())) return; sahne.secimTemizle(); sahne.sec({f->sonucDugum(), 0}, /*coklu*/ false); } // Mevcut yuzeySisir feature'ına yeni hedef(ler) ekle (parametrik güncelleme — düğüm aynı // kalır, geometri yenilenir). Eski tek-hedef paramları (hedefX/Y/Z+miktar) h0/m0'a taşınır. inline void gsdSisirHedefEkle(gsd::GsdSistemi& gsdSis, u32 fk, const std::vector>& yeni) { gsd::GsdFeature* f = gsdSis.bul(fk); if (!f || yeni.empty()) return; // TOPLU ata → TEK yeniden-insa (param başına rebuild NURBS'te donduruyordu). std::vector> d; int n = (int)(f->p("hedefSayi", 0.0) + 0.5); if (n == 0) { // eski tek-hedef biçimi → h0/m0'a göç d.push_back({"h0x", f->p("hedefX")}); d.push_back({"h0y", f->p("hedefY")}); d.push_back({"h0z", f->p("hedefZ")}); d.push_back({"m0", f->p("miktar", 5.0)}); n = 1; } for (const auto& [p, m] : yeni) { const std::string k = std::to_string(n++); d.push_back({"h" + k + "x", (f64)p.x}); d.push_back({"h" + k + "y", (f64)p.y}); d.push_back({"h" + k + "z", (f64)p.z}); d.push_back({"m" + k, (f64)m}); } d.push_back({"hedefSayi", (f64)n}); gsdSis.gsdGuncelleToplu(fk, d); } // Çok-nokta şişir editörü commit'i: seçili grid noktaları + SAKLI (nokta başına farklı) // miktarlarla tek parametrik yuzeySisir feature. Tüm miktarlar ~0 → feature açılmaz (false). inline bool gsdSisirCokUygula(GsdGizmoDurum& gz, Sahne& sahne, Cekirdek& cekirdek, gsd::GsdSistemi& gsdSis) { bool var = false; for (f32 m : gz.sisirMik) if (std::abs(m) > 1e-3f) { var = true; break; } if (!var || gz.sisirIdx.empty()) return false; const u32 kaynak = gz.gridDugum; // Seçili düğüm bir yuzeySisir SONUCU ise VEYA bu kaynağın zaten şişir feature'ı varsa // (seçim eski kaynakta kaldıysa): yeni feature yığma, MEVCUDA hedef ekle. u32 ef = gsdAyniTipFeature(gsdSis, kaynak, "yuzeySisir"); if (!ef) ef = gsdKaynakliFeature(gsdSis, kaynak, "yuzeySisir"); if (ef) { std::vector> yeni; for (size_t i = 0; i < gz.sisirIdx.size(); ++i) if (std::abs(gz.sisirMik[i]) > 1e-3f) yeni.push_back({gz.gridDunya[gz.sisirIdx[i]], gz.sisirMik[i]}); gsdSisirHedefEkle(gsdSis, ef, yeni); gz.sisirIdx.clear(); gz.sisirMik.clear(); gz.sisirAktif = -1; gz.gridDugum = 0; gsdSonucuSec(gsdSis, sahne, ef); dmTutamacOlayiYay(cekirdek, "Yüzey", "Yüzey Şişir"); char tb[48]; std::snprintf(tb, sizeof(tb), "Şişirme güncellendi (+%d tepe)", (int)yeni.size()); ui::toastGoster(tb, ui::ToastTip::Basari); return true; } f32 yaricap = 20.0f; if (Dugum* dn = sahne.bul(kaynak)) yaricap = std::max(dn->dunyaYaricap() * dogrudanAyar().gsdSisirYaricapOran, 0.5f); const u32 fk = gsdSis.gsdBaslat("yuzeySisir", VARLIK_YOK, kaynak); if (!fk) { ui::toastGoster("Şişirme başarısız", ui::ToastTip::Hata); return false; } std::vector> d{ {"yaricap", (f64)yaricap}, {"hedefSayi", (f64)gz.sisirIdx.size()}, // Bağlı kenar koruması (Fikri 07-22): ayar varsayılanı yeni feature'a param olarak // yazılır → parametrik/undo'lu; panelden feature bazında değiştirilebilir. {"sinirKilit", dogrudanAyar().gsdSisirSinirKilit ? 1.0 : 0.0}, {"kilitDerinlik", (f64)dogrudanAyar().gsdSisirKilitDerinlik}}; for (size_t i = 0; i < gz.sisirIdx.size(); ++i) { const Vec3f& p = gz.gridDunya[gz.sisirIdx[i]]; const std::string k = std::to_string(i); d.push_back({"h" + k + "x", (f64)p.x}); d.push_back({"h" + k + "y", (f64)p.y}); d.push_back({"h" + k + "z", (f64)p.z}); d.push_back({"m" + k, (f64)gz.sisirMik[i]}); } gsdSis.gsdGuncelleToplu(fk, d); // TEK yeniden-insa (param başına rebuild = donma) gz.sisirIdx.clear(); gz.sisirMik.clear(); gz.sisirAktif = -1; gz.gridDugum = 0; gsdSonucuSec(gsdSis, sahne, fk); // seçim sonuca → sonraki şişirme AYNI feature'ı günceller dmTutamacOlayiYay(cekirdek, "Yüzey", "Yüzey Şişir"); ui::toastGoster("Yüzey şişirildi (çok nokta)", ui::ToastTip::Basari); return true; } // Gizmo hedef bilgisi: anchor + 1 (yüzey/tel: tek normal) veya 3 (kenar: dik/düz/yan) yön. struct GsdHedef { u32 dugum = 0; Vec3f merkez{}; bool tel = false; // GSD Tel (extrude) bool kenarMod = false; // tek kenar uzat u32 kenarOrd = 0; Vec3f yon[3]{}; // dünya yönleri (kenarMod: [0]=dik(N) [1]=düz(dışa) [2]=yan) int yonSayi = 0; }; // Seçili tek GSD düğümünü çöz. Yüzey → 1 yön (normal); Tel → 1 yön (düzlem normali); // Kenar kipinde yüzey kenarı → 3 yön (dik/düz/yan) tek-kenar uzatma için. Yoksa false. inline bool gsdGizmoHedef(Sahne& sahne, GsdHedef& h) { h = GsdHedef{}; if (sahne.secililer().size() != 1) return false; const SecimOge sel = sahne.secililer().back(); const u32 g = sel.govde; Dugum* d = sahne.bul(g); if (!d || !d->brep || !d->brep->gecerliMi()) return false; const auto dunyaN = [&](const Vec3d& v) { const Vec3d w = d->donusum.yonelim.dondur(v); return Vec3f{(f32)w.x, (f32)w.y, (f32)w.z}.birim(); }; Vec3d m; // anchor (yerel) if (d->tip == DugumTip::Yuzey) { // Kalınlaştır sonucu: düğüm Yuzey ama brep KATI (doluMu) → yüzey op'ları (offset/uzat/ // şişir) anlamsız; gizmo çıkmaz, kenarda fillet/pah (R/C) devralır (çifte gizmo olmaz). if (d->brep->doluMu()) return false; geometri::YuzBilgi yb; // HER KARE çağrılır → alanHesapla=false ŞART: SurfaceProperties B-spline yüzde (şişmiş/ // kenar-kaydırılmış sonuç) kare başına yüzlerce ms sürüyordu = kenar op'larındaki DONMA. // merkez ucuz UV-orta fallback ile dolar (yuzBilgi içinde), normal zaten hesaplanır. if (!d->brep->yuzBilgi(1, yb, false)) return false; m = yb.merkez; // Kenar kipinde kenar seçili → 3-yönlü tek-kenar uzat (dik/düz/yan). if (sahne.kip() == SecimKip::Kenar && sel.alt != 0) { geometri::KenarBilgi kb; if (d->brep->kenarBilgi(sel.alt, kb)) { const Vec3d N = yb.normal; Vec3d ec = kb.orta - yb.merkez; Vec3d duz = ec - N * ec.nokta(N); // dışa-düzlem (düz uzat) if (duz.uzunluk() < 1e-6) duz = (kb.son - kb.bas); // dejenere → kenar boyunca if (duz.uzunluk() > 1e-9) { duz = duz.birim(); const Vec3d yan = N.capraz(duz).birim(); // üçüncü ortogonal (kenar boyu) m = kb.orta; h.kenarMod = true; h.kenarOrd = sel.alt; h.yon[0] = dunyaN(N); h.yon[1] = dunyaN(duz); h.yon[2] = dunyaN(yan); h.yonSayi = 3; } } } if (!h.kenarMod) { h.yon[0] = dunyaN(yb.normal); h.yonSayi = 1; } } else if (d->tip == DugumTip::Tel) { Vec3d mm, n; if (!geometri::telDuzlemNormal(*d->brep, mm, n)) return false; m = mm; h.tel = true; h.yon[0] = dunyaN(n); h.yonSayi = 1; if (sel.alt != 0) { // tel kenarına tıkla → oku kenara demirle geometri::KenarBilgi kb; if (d->brep->kenarBilgi(sel.alt, kb)) m = kb.orta; } } else { return false; } const Vec3d mw = d->donusum.noktaUygula(m); h.merkez = {(f32)mw.x, (f32)mw.y, (f32)mw.z}; h.dugum = g; return true; } inline void gsdGizmoCizVeIsle(GsdGizmoDurum& gz, Sahne& sahne, Cekirdek& cekirdek, gsd::GsdSistemi& gsdSis, OrbitKamera& kamera, ImDrawList* dl, ImGuiIO& io, bool vpHover, f32 mx, f32 my, f32 vw, f32 vh, f32 enBoy, bool& ustunde) { ustunde = false; if (!dogrudanAyar().gsdGizmoAcik) return; const Mat4f vp = kamera.izdusum(enBoy) * kamera.gorunum(); const auto proj = [&](const Vec3f& p, ImVec2& s) -> bool { const Vec4f h = vp * Vec4f{p, 1.0f}; if (h.w <= 1e-4f) return false; s = ImVec2(mx + (h.x / h.w * 0.5f + 0.5f) * vw, my + (h.y / h.w * 0.5f + 0.5f) * vh); return true; }; const auto okBasi = [&](ImVec2 taban, ImVec2 uc, ImU32 renk) { f32 dx = uc.x - taban.x, dy = uc.y - taban.y; const f32 L = std::sqrt(dx * dx + dy * dy); if (L < 1.0f) return; dx /= L; dy /= L; const f32 tb = dogrudanAyar().tutamacBoy, u = 9.0f * tb, w2 = 3.5f * tb; dl->AddTriangleFilled(uc, ImVec2(uc.x - dx * u - dy * w2, uc.y - dy * u + dx * w2), ImVec2(uc.x - dx * u + dy * w2, uc.y - dy * u - dx * w2), renk); }; // ---- SÜRÜKLEME DEĞİL: seçili GSD düğümde yön ok(ları) + hit-test ---- if (!gz.surukluyor) { GsdHedef h; if (!gsdGizmoHedef(sahne, h)) return; const f32 L = std::max(kamera.mesafe() * dogrudanAyar().okBoyOran, 0.03f); ImVec2 s0Base; const bool baseOk = proj(h.merkez, s0Base); int yakalanan = -1; // yakalanan ok yönü (kenarMod: 0=aim 1=yan; genel: yön idx) Vec3f yakalananYon{}; bool herhangiYakin = false; // ================= KENAR MODU: aim oku (döndürülebilir) + yan ok + DÖNME HALKASI ========= if (h.kenarMod && baseOk) { const Vec3f N = h.yon[0], duz = h.yon[1], yan = h.yon[2]; // Tilt düzleminin ekran temeli (halka + açı çözümü için). ImVec2 sDuzP, sNP; const bool okBaz = proj(h.merkez + duz * L, sDuzP) && proj(h.merkez + N * L, sNP); const ImVec2 bDuz{sDuzP.x - s0Base.x, sDuzP.y - s0Base.y}; const ImVec2 bN{sNP.x - s0Base.x, sNP.y - s0Base.y}; const f32 det = bDuz.x * bN.y - bDuz.y * bN.x; // Halka SÜRÜKLEMESİ aktifse: açıyı fareden çöz (mutlak). GİZMOYU GİZLEME — // sadece açıyı güncelle, çizime devam et (eski kod return ile tüm gizmoyu // yok ediyordu → döndürürken kayboluyordu). aim aşağıda güncel açıdan hesaplanır. if (gz.aciSurukle) { ustunde = true; gz.tutamacYakin = true; if (okBaz && std::abs(det) > 1e-3f) { const ImVec2 rel{io.MousePos.x - s0Base.x, io.MousePos.y - s0Base.y}; const f32 a = (rel.x * bN.y - rel.y * bN.x) / det; // düz bileşeni const f32 bb = (bDuz.x * rel.y - bDuz.y * rel.x) / det; // dik bileşeni gz.kenarAci = std::atan2(bb, a); } if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) gz.aciSurukle = false; } const Vec3f aim = duz * std::cos(gz.kenarAci) + N * std::sin(gz.kenarAci); // tilt: 0=düz π/2=dik // Dönme halkası (tilt düzleminde elips ~ bDuz/bN). OKLARIN DIŞINDA (1.15×) → tutamaç ok // ucunun ötesinde, ok gövdesiyle çakışmaz. Tutamaç = aim yönünde. const f32 hf = 1.15f; if (okBaz && std::abs(det) > 1e-3f) { const int seg = 48; for (int i = 0; i < seg; ++i) { const f32 a0 = (f32)i / seg * 6.2831853f, a1 = (f32)(i + 1) / seg * 6.2831853f; const ImVec2 p0{s0Base.x + (bDuz.x * std::cos(a0) + bN.x * std::sin(a0)) * hf, s0Base.y + (bDuz.y * std::cos(a0) + bN.y * std::sin(a0)) * hf}; const ImVec2 p1{s0Base.x + (bDuz.x * std::cos(a1) + bN.x * std::sin(a1)) * hf, s0Base.y + (bDuz.y * std::cos(a1) + bN.y * std::sin(a1)) * hf}; dl->AddLine(p0, p1, IM_COL32(150, 190, 255, 90), 1.4f); } const ImVec2 hp{s0Base.x + (bDuz.x * std::cos(gz.kenarAci) + bN.x * std::sin(gz.kenarAci)) * hf, s0Base.y + (bDuz.y * std::cos(gz.kenarAci) + bN.y * std::sin(gz.kenarAci)) * hf}; const f32 hd2 = (io.MousePos.x - hp.x) * (io.MousePos.x - hp.x) + (io.MousePos.y - hp.y) * (io.MousePos.y - hp.y); const f32 hr = 11.0f * dokunmaHedefCarpani(); const bool halkaYakin = vpHover && hd2 <= hr * hr; dl->AddCircleFilled(hp, halkaYakin ? 7.0f : 5.0f, IM_COL32(150, 190, 255, 255)); if (halkaYakin) dl->AddCircle(hp, 10.0f, IM_COL32(150, 190, 255, 255), 0, 1.6f); // hedef halkası if (halkaYakin) { gz.tutamacYakin = true; ustunde = true; // halka üstünde → orbit/ok yakalama bastır dl->AddText(ImGui::GetFont(), 11.5f, {hp.x + 10.0f, hp.y - 6.0f}, IM_COL32(150, 190, 255, 255), "↻ döndür"); // Halkaya tıklama OKA ÖNCELİKLİ: aim oku halka topuyla ÜST ÜSTE → tıkla=döndür (uzat DEĞİL). if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { gz.aciSurukle = true; return; } } } // İki ok: aim (döndürülmüş, mor) ve yan (kenar boyu, amber). const Vec3f oklar[2] = {aim, yan}; const ImU32 okRenk[2] = {IM_COL32(180, 170, 255, 255), IM_COL32(230, 200, 120, 255)}; const char* okAd[2] = {"uzat", "yan"}; for (int ai = 0; ai < 2; ++ai) { ImVec2 s0, s1; if (!(proj(h.merkez, s0) && proj(h.merkez + oklar[ai] * L, s1))) continue; const f32 dx = s1.x - s0.x, dy = s1.y - s0.y, L2 = dx * dx + dy * dy; f32 t = L2 > 1e-6f ? ((io.MousePos.x - s0.x) * dx + (io.MousePos.y - s0.y) * dy) / L2 : 0.0f; t = t < 0.0f ? 0.0f : (t > 1.0f ? 1.0f : t); const f32 qx = s0.x + t * dx - io.MousePos.x, qy = s0.y + t * dy - io.MousePos.y; const f32 hr = 12.0f * dokunmaHedefCarpani(); const bool okYakin = vpHover && (qx * qx + qy * qy) <= hr * hr; herhangiYakin = herhangiYakin || okYakin; const ImU32 renk = okYakin ? okRenk[ai] : (okRenk[ai] & 0x00FFFFFF) | 0xB4000000; if (okYakin && dogrudanAyar().glowAcik) gizmo::glowCizgi(dl, s0, s1, renk, 3.2f); else dl->AddLine(s0, s1, renk, okYakin ? 3.2f : 2.4f); okBasi(s0, s1, renk); dl->AddCircleFilled(s0, (okYakin ? 4.0f : 3.0f) * dogrudanAyar().tutamacBoy, renk); dl->AddText(ImGui::GetFont(), 11.5f, {s1.x + 5.0f, s1.y - 6.0f}, renk, okAd[ai]); if (okYakin && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { yakalanan = ai; yakalananYon = oklar[ai]; } } gz.tutamacYakin = gz.tutamacYakin || herhangiYakin; ustunde = ustunde || (herhangiYakin && ImGui::IsMouseDown(ImGuiMouseButton_Left)); // FARKINDALIK: kalıcı bağlam okuması (açı + jest ipucu) — her an ne yapabileceğin belli. { const int aciDer = (int)std::lround(gz.kenarAci * 57.2958f); char ctx[64]; std::snprintf(ctx, sizeof(ctx), "kenar %d° ↻ döndür · ⇢ çek", aciDer); dl->AddText(ImGui::GetFont(), 12.0f, {s0Base.x + 10.0f, s0Base.y + 14.0f}, IM_COL32(150, 190, 255, 220), ctx); } // Kenar modu çipleri: Uzat (yeni strip) ↔ Kaydır (yüzeyi reshape — kenarı it-çek). { ImFont* fnt = ImGui::GetFont(); const float fs = 12.5f, py = 3.0f; float cx = s0Base.x + 10.0f, cy = s0Base.y + 30.0f; const char* etk[2] = {"Uzat", "Kaydır"}; for (int mi = 0; mi < 2; ++mi) { const bool aktif = (mi == 1) == gz.kenarKaydirMod; const ImVec2 ts = fnt->CalcTextSizeA(fs, FLT_MAX, 0.0f, etk[mi]); const ImVec2 mn{cx, cy}, mx2{cx + ts.x + 12.0f, cy + ts.y + py * 2.0f}; const bool hov = vpHover && io.MousePos.x >= mn.x && io.MousePos.x <= mx2.x && io.MousePos.y >= mn.y && io.MousePos.y <= mx2.y; const ImU32 mr = IM_COL32(150, 190, 255, 255); dl->AddRectFilled(mn, mx2, IM_COL32(24, 26, 30, aktif ? 240 : 190), 5.0f); dl->AddRect(mn, mx2, aktif ? mr : ((mr & 0x00FFFFFF) | (hov ? 0xA0000000 : 0x55000000)), 5.0f, 0, aktif ? 1.8f : 1.0f); dl->AddText(fnt, fs, {mn.x + 6.0f, mn.y + py}, aktif ? mr : IM_COL32(200, 205, 210, hov ? 255 : 180), etk[mi]); if (hov) { ustunde = true; if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) gz.kenarKaydirMod = (mi == 1); } cx = mx2.x + 4.0f; } } if (yakalanan >= 0) { gz.surukluyor = true; gz.dugum = h.dugum; gz.tel = false; gz.kenarMod = true; gz.kenarOrd = h.kenarOrd; gz.mesafe = 0; gz.tipBuf[0] = 0; gz.merkezD = h.merkez; gz.normalD = yakalananYon; } return; } // ===== ŞİŞİRME IZGARASI (Plasticity çok-noktalı): şişir modunda yüzeye pick edilebilir // nokta ızgarası çiz; tıklanan nokta şişirme ÇIPASI olur (yoksa merkez = eski davranış). ===== Vec3f sisirKok = h.merkez; const bool sisirMod = (gz.mod == 2 && !h.tel && h.yonSayi == 1); if (sisirMod) { if (gz.gridDugum != h.dugum) { // düğüm değişti → yeniden örnekle (feature-time, ucuz) gz.gridDugum = h.dugum; gz.sisirIdx.clear(); gz.sisirMik.clear(); gz.sisirAktif = -1; gz.gridDunya.clear(); gz.gridN = 0; if (Dugum* dn = sahne.bul(h.dugum); dn && dn->brep) { int nb = (int)dogrudanAyar().gsdSisirGridBol; nb = nb < 2 ? 2 : (nb > 64 ? 64 : nb); std::vector yerel; if (geometri::yuzeyOrneklemGrid(*dn->brep, nb, nb, yerel)) { gz.gridDunya.reserve(yerel.size()); for (const Vec3d& p : yerel) { const Vec3d w = dn->donusum.noktaUygula(p); gz.gridDunya.push_back({(f32)w.x, (f32)w.y, (f32)w.z}); } gz.gridN = nb; } } } int enYakin = -1; f32 enD2 = 1e18f; // Sınır kilidi açıkken KENAR grid noktaları şişirmede etkisiz (pole halkası sabit) → // pick DIŞI + çok soluk çizilir (kullanıcı doğal olarak iç noktaları seçer; kenarı // taşımak isteyen kenar kipinde Kenar Taşı kullanır). Kilit kapalı = eski davranış. const int gN = gz.gridN; const bool gKilit = dogrudanAyar().gsdSisirSinirKilit && gN > 2; for (size_t gi = 0; gi < gz.gridDunya.size(); ++gi) { ImVec2 sp; if (!proj(gz.gridDunya[gi], sp)) continue; if (gKilit) { const int giI = (int)gi % gN, giJ = (int)gi / gN; if (giI == 0 || giI == gN - 1 || giJ == 0 || giJ == gN - 1) { dl->AddCircleFilled(sp, 2.0f, IM_COL32(120, 220, 235, 45)); continue; } } const f32 d2 = (io.MousePos.x - sp.x) * (io.MousePos.x - sp.x) + (io.MousePos.y - sp.y) * (io.MousePos.y - sp.y); const auto it = std::find(gz.sisirIdx.begin(), gz.sisirIdx.end(), (int)gi); const bool sec = it != gz.sisirIdx.end(); const bool aktif = sec && (int)(it - gz.sisirIdx.begin()) == gz.sisirAktif; const f32 hr = 8.0f * dokunmaHedefCarpani(); const bool yak = vpHover && d2 <= hr * hr; if (yak && d2 < enD2) { enD2 = d2; enYakin = (int)gi; } const ImU32 c = aktif ? IM_COL32(255, 255, 255, 255) : sec ? IM_COL32(120, 220, 235, 255) : yak ? IM_COL32(190, 240, 250, 235) : IM_COL32(120, 220, 235, 190); const f32 pr = sec ? 5.0f : yak ? 4.5f : 2.8f; // Koyu hale: seçim vurgusu (parlayan yüzey) üstünde de nokta net okunsun. dl->AddCircleFilled(sp, pr + 1.6f, IM_COL32(12, 22, 28, 210)); dl->AddCircleFilled(sp, pr, c); if (sec) dl->AddCircle(sp, aktif ? 9.0f : 8.0f, c, 0, aktif ? 2.0f : 1.4f); // Saklı miktar rozeti (editör modu): noktanın yanında kendi mm değeri. if (sec) { const size_t pos = (size_t)(it - gz.sisirIdx.begin()); if (pos < gz.sisirMik.size() && std::abs(gz.sisirMik[pos]) > 1e-3f) { char mb[16]; std::snprintf(mb, sizeof(mb), "%.1f", gz.sisirMik[pos]); dl->AddText(ImGui::GetFont(), 11.0f, {sp.x + 8.0f, sp.y - 14.0f}, c, mb); } } } if (enYakin >= 0) { ustunde = true; if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { // TOGGLE seç/kaldır → oku KAPMA auto it = std::find(gz.sisirIdx.begin(), gz.sisirIdx.end(), enYakin); if (it != gz.sisirIdx.end()) { const int pos = (int)(it - gz.sisirIdx.begin()); gz.sisirIdx.erase(it); gz.sisirMik.erase(gz.sisirMik.begin() + pos); if (gz.sisirAktif == pos) gz.sisirAktif = gz.sisirIdx.empty() ? -1 : (int)gz.sisirIdx.size() - 1; else if (gz.sisirAktif > pos) --gz.sisirAktif; } else { gz.sisirIdx.push_back(enYakin); gz.sisirMik.push_back(0.0f); gz.sisirAktif = (int)gz.sisirIdx.size() - 1; // yeni nokta aktif olur } gz.tutamacYakin = true; return; } } // SERBEST NOKTA (grid dışı): Ctrl+tık → imleç-altı yüzey noktası (CPU raycast) hedef // olur. gridDunya'ya EKLENİR (ızgara tel bağlantısı ilk nb×nb ile sınırlı → çizim güvenli); // seçim/miktar/önizleme yolları grid noktasıyla birebir aynı çalışır. if (enYakin < 0 && vpHover && io.KeyCtrl && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { Vec3f o, dyon; { // ekran → dünya ışını (yuz_tasi fareIsin ikizi; reverse-Z near=1 far=0) const f32 ndcX = 2.0f * (io.MousePos.x - mx) / vw - 1.0f; const f32 ndcY = 2.0f * (io.MousePos.y - my) / vh - 1.0f; const Mat4f invVP = vp.ters(); const Vec4f yH = invVP * Vec4f{ndcX, ndcY, 1.0f, 1.0f}; const Vec4f uH = invVP * Vec4f{ndcX, ndcY, 0.0f, 1.0f}; const Vec3f yakin = yH.xyz() * (1.0f / yH.w); const Vec3f uzak = uH.xyz() * (1.0f / uH.w); o = yakin; dyon = (uzak - yakin).birim(); } Sahne::IsinVurus vur; if (sahne.isinVur(o, dyon, vur) && vur.govde == h.dugum) { gz.gridDunya.push_back(vur.nokta); gz.sisirIdx.push_back((int)gz.gridDunya.size() - 1); gz.sisirMik.push_back(0.0f); gz.sisirAktif = (int)gz.sisirIdx.size() - 1; gz.tutamacYakin = true; ustunde = true; return; } } // Çıpa = AKTİF nokta (ok orada; sürükleme ONUN miktarını değiştirir). if (gz.sisirAktif >= 0 && gz.sisirAktif < (int)gz.sisirIdx.size() && gz.sisirIdx[gz.sisirAktif] < (int)gz.gridDunya.size()) sisirKok = gz.gridDunya[gz.sisirIdx[gz.sisirAktif]]; // SAKLI miktarların statik önizlemesi (sürükleme yokken de birikmiş tepeler görünsün). bool sakliVar = false; for (f32 m : gz.sisirMik) if (std::abs(m) > 1e-3f) { sakliVar = true; break; } if (sakliVar && gz.gridN >= 2) { f32 yaricap = 20.0f; if (Dugum* dn = sahne.bul(h.dugum)) yaricap = std::max(dn->dunyaYaricap() * dogrudanAyar().gsdSisirYaricapOran, 0.5f); const int nb = gz.gridN; const ImU32 onc = IM_COL32(120, 220, 235, 150); std::vector ekr(gz.gridDunya.size()); std::vector gecerli(gz.gridDunya.size(), 0); for (size_t i = 0; i < gz.gridDunya.size(); ++i) { const Vec3f& p = gz.gridDunya[i]; f32 acc = 0.0f; for (size_t si = 0; si < gz.sisirIdx.size(); ++si) { const Vec3f& hp = gz.gridDunya[gz.sisirIdx[si]]; const f32 dx = p.x - hp.x, dy = p.y - hp.y, dz = p.z - hp.z; const f32 d = std::sqrt(dx * dx + dy * dy + dz * dz); if (d >= yaricap) continue; const f32 x = d / yaricap, w = (1.0f - x * x) * (1.0f - x * x); acc += gz.sisirMik[si] * w; } ImVec2 sp; if (proj(p + h.yon[0] * acc, sp)) { ekr[i] = sp; gecerli[i] = 1; } } for (int j = 0; j < nb; ++j) for (int i = 0; i < nb; ++i) { const int id = j * nb + i; if (!gecerli[id]) continue; if (i + 1 < nb && gecerli[id + 1]) dl->AddLine(ekr[id], ekr[id + 1], onc, 1.0f); if (j + 1 < nb && gecerli[id + nb]) dl->AddLine(ekr[id], ekr[id + nb], onc, 1.0f); } } if (gz.sisirIdx.size() > 1) { ImVec2 sp; if (proj(sisirKok, sp)) { char b[64]; std::snprintf(b, sizeof(b), "%d nokta · sürükle=aktif · Enter=uygula", (int)gz.sisirIdx.size()); dl->AddText(ImGui::GetFont(), 11.5f, {sp.x + 10.0f, sp.y + 8.0f}, IM_COL32(120, 220, 235, 255), b); } // ÇOK-NOKTA EDİTÖRÜ: Enter = tüm noktaları kendi saklı miktarıyla uygula; ESC = vazgeç. if (ImGui::IsKeyPressed(ImGuiKey_Enter)) gsdSisirCokUygula(gz, sahne, cekirdek, gsdSis); if (ImGui::IsKeyPressed(ImGuiKey_Escape)) { gz.sisirIdx.clear(); gz.sisirMik.clear(); gz.sisirAktif = -1; } } } // ================= YÜZEY / TEL: tek ok ================= for (int ai = 0; ai < h.yonSayi; ++ai) { ImVec2 s0, s1; if (!(proj(sisirKok, s0) && proj(sisirKok + h.yon[ai] * L, s1))) continue; const f32 dx = s1.x - s0.x, dy = s1.y - s0.y, L2 = dx * dx + dy * dy; f32 t = L2 > 1e-6f ? ((io.MousePos.x - s0.x) * dx + (io.MousePos.y - s0.y) * dy) / L2 : 0.0f; t = t < 0.0f ? 0.0f : (t > 1.0f ? 1.0f : t); const f32 qx = s0.x + t * dx - io.MousePos.x, qy = s0.y + t * dy - io.MousePos.y; const f32 hr = 12.0f * dokunmaHedefCarpani(); const bool okYakin = vpHover && (qx * qx + qy * qy) <= hr * hr; herhangiYakin = herhangiYakin || okYakin; const ImU32 taban = h.tel ? IM_COL32(165, 155, 235, 255) : gsdModRenk(gz.mod); const ImU32 renk = okYakin ? taban : (taban & 0x00FFFFFF) | 0xB4000000; const bool glow = okYakin && dogrudanAyar().glowAcik; if (glow) gizmo::glowCizgi(dl, s0, s1, renk, 3.2f); else dl->AddLine(s0, s1, renk, okYakin ? 3.2f : 2.4f); okBasi(s0, s1, renk); dl->AddCircleFilled(s0, (okYakin ? 4.0f : 3.0f) * dogrudanAyar().tutamacBoy, renk); if (okYakin) { char hb[40]; std::snprintf(hb, sizeof(hb), "sürükle: %s", h.tel ? "yüzeye çek" : gsdModEtiket(gz.mod)); gizmo::degerEtiketi(dl, {s1.x + 8.0f, s1.y}, hb, renk); } if (okYakin && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { yakalanan = ai; yakalananYon = h.yon[ai]; } } gz.tutamacYakin = herhangiYakin; ustunde = herhangiYakin && ImGui::IsMouseDown(ImGuiMouseButton_Left); // ---- Mod çipleri (KLAVYESİZ): yalnız TÜM YÜZEY (tel değil) — Ofset/Uzat ---- if (baseOk && h.yonSayi == 1 && !h.tel) { ImFont* fnt = ImGui::GetFont(); const float fs = 12.5f, py = 3.0f; float cx = s0Base.x - 2.0f, cy = s0Base.y + 16.0f; for (int mi = 0; mi < 4; ++mi) { const char* lbl = gsdModEtiket(mi); const ImVec2 ts = fnt->CalcTextSizeA(fs, FLT_MAX, 0.0f, lbl); const ImVec2 mn{cx, cy}, mx{cx + ts.x + 12.0f, cy + ts.y + py * 2.0f}; const bool hov = vpHover && io.MousePos.x >= mn.x && io.MousePos.x <= mx.x && io.MousePos.y >= mn.y && io.MousePos.y <= mx.y; const bool aktif = gz.mod == mi; const ImU32 mr = gsdModRenk(mi); dl->AddRectFilled(mn, mx, IM_COL32(24, 26, 30, aktif ? 240 : 190), 5.0f); dl->AddRect(mn, mx, aktif ? mr : ((mr & 0x00FFFFFF) | (hov ? 0xA0000000 : 0x55000000)), 5.0f, 0, aktif ? 1.8f : 1.0f); dl->AddText(fnt, fs, {mn.x + 6.0f, mn.y + py}, aktif ? mr : IM_COL32(200, 205, 210, hov ? 255 : 180), lbl); if (hov) { ustunde = true; if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) gz.mod = mi; } cx = mx.x + 4.0f; } } if (yakalanan >= 0) { gz.surukluyor = true; gz.dugum = h.dugum; gz.tel = h.tel; gz.kenarMod = false; // bu yol yüzey/tel (kenar modu yukarıda ele alındı) gz.kenarOrd = 0; // Çok-nokta editörü: sürükleme AKTİF noktanın SAKLI miktarından devam eder (0'dan değil). gz.mesafe = (sisirMod && gz.sisirIdx.size() >= 2 && gz.sisirAktif >= 0 && gz.sisirAktif < (int)gz.sisirMik.size()) ? gz.sisirMik[gz.sisirAktif] : 0.0f; gz.tipBuf[0] = 0; gz.merkezD = sisirKok; // şişir modunda seçili ızgara noktası; diğer modlarda = h.merkez gz.normalD = yakalananYon; } return; } // ---- SÜRÜKLEME: canlı offset önizleme ---- ustunde = true; ImVec2 c0, c1; if (proj(gz.merkezD, c0) && proj(gz.merkezD + gz.normalD, c1)) { const f32 vx = c1.x - c0.x, vy = c1.y - c0.y, L2 = vx * vx + vy * vy; if (L2 > 1e-6f) gz.mesafe += (io.MouseDelta.x * vx + io.MouseDelta.y * vy) / L2; } // Sürüklerken sayı yaz → hassas mesafe (drag override). for (int i = 0; i < io.InputQueueCharacters.Size; ++i) { const ImWchar c = io.InputQueueCharacters[i]; const size_t l = std::strlen(gz.tipBuf); if (((c >= '0' && c <= '9') || c == '.' || (c == '-' && l == 0)) && l < sizeof(gz.tipBuf) - 1) { gz.tipBuf[l] = (char)c; gz.tipBuf[l + 1] = 0; } } if (ImGui::IsKeyPressed(ImGuiKey_Backspace)) { const size_t l = std::strlen(gz.tipBuf); if (l) gz.tipBuf[l - 1] = 0; } const bool tipVar = gz.tipBuf[0] && !(gz.tipBuf[0] == '-' && !gz.tipBuf[1]); if (tipVar) gz.mesafe = (f32)std::atof(gz.tipBuf); // Mod: kenarMod → tek kenar uzat; tel → extrude; tüm yüzey → gz.mod (0=offset 1=uzat 2=şişir). // Kalınlaştır gizmoda YOK (sağ menüde ayrı komut). const bool uzat = !gz.tel && !gz.kenarMod && gz.mod == 1; const bool sisir = !gz.tel && !gz.kenarMod && gz.mod == 2; const bool kalinlastir = !gz.tel && !gz.kenarMod && gz.mod == 3; const char* modAd = gz.kenarMod ? "↦ kenar uzat" : gz.tel ? "⇑ extrude" : sisir ? "◒ şişir" : kalinlastir ? "▤ kalınlaştır" : uzat ? "uzat" : "offset"; const ImU32 modRenk = (gz.tel || gz.kenarMod) ? IM_COL32(180, 170, 255, 255) : gsdModRenk(gz.mod); // Önizleme: kaynağın konturlarını normal boyunca mesafe kadar ötele (ucuz wireframe). // Yüzey → yüz konturu (kapalı loop); Tel → her kenarın noktaları (açık zincir). const ImU32 col = modRenk; const ImU32 bag = (col & 0x00FFFFFF) | 0x70000000; // aynı renk, düşük alfa (bağ çizgileri) // CANLI ŞİŞİRME ÖNİZLEME: deforme ızgarayı GERÇEK kubbe şekliyle çiz (ghost kontur DEĞİL — // motor falloff'unun birebir görsel ikizi). Render pipeline'ına dokunmaz, yalnız ImDrawList. bool onizlemeCizildi = false; if (sisir && gz.gridN >= 2 && (int)gz.gridDunya.size() >= gz.gridN * gz.gridN) { f32 yaricap = 20.0f; if (Dugum* dn = sahne.bul(gz.dugum)) yaricap = std::max(dn->dunyaYaricap() * dogrudanAyar().gsdSisirYaricapOran, 0.5f); const int nb = gz.gridN; // Hedefler NOKTA+MİKTAR çifti: aktif nokta canlı sürükleme miktarını, diğerleri kendi // SAKLI miktarını kullanır (per-nokta farklı miktar editörü). Boş seçim → tek çıpa (merkezD). std::vector> hed; if (gz.sisirIdx.empty()) { hed.push_back({gz.merkezD, gz.mesafe}); } else { for (size_t si = 0; si < gz.sisirIdx.size(); ++si) { const int gi = gz.sisirIdx[si]; if (gi < 0 || gi >= (int)gz.gridDunya.size()) continue; const f32 mik = ((int)si == gz.sisirAktif) ? gz.mesafe : (si < gz.sisirMik.size() ? gz.sisirMik[si] : 0.0f); hed.push_back({gz.gridDunya[gi], mik}); } } std::vector ekr(gz.gridDunya.size()); std::vector gecerli(gz.gridDunya.size(), 0); for (size_t i = 0; i < gz.gridDunya.size(); ++i) { const Vec3f& p = gz.gridDunya[i]; f32 acc = 0.0f; for (const auto& [hp, mik] : hed) { const f32 dx = p.x - hp.x, dy = p.y - hp.y, dz = p.z - hp.z; const f32 d = std::sqrt(dx * dx + dy * dy + dz * dz); if (d >= yaricap) continue; const f32 x = d / yaricap, w = (1.0f - x * x) * (1.0f - x * x); acc += mik * w; // motorla aynı falloff (tepe=1, kenar=0) } ImVec2 sp; if (proj(p + gz.normalD * acc, sp)) { ekr[i] = sp; gecerli[i] = 1; } } for (int j = 0; j < nb; ++j) for (int i = 0; i < nb; ++i) { const int id = j * nb + i; if (!gecerli[id]) continue; if (i + 1 < nb && gecerli[id + 1]) dl->AddLine(ekr[id], ekr[id + 1], col, 1.4f); if (j + 1 < nb && gecerli[id + nb]) dl->AddLine(ekr[id], ekr[id + nb], col, 1.4f); } for (const auto& [hp, mik] : hed) { // hedef tepe noktaları vurgula ImVec2 sp; if (proj(hp + gz.normalD * mik, sp)) dl->AddCircleFilled(sp, 3.5f, col); } onizlemeCizildi = true; } if (!onizlemeCizildi) if (Dugum* d = sahne.bul(gz.dugum); d && d->brep) { std::vector> loops; if (gz.kenarMod) { loops.push_back(d->brep->kenarNoktalari(gz.kenarOrd)); // yalnız uzatılan kenar } else if (gz.tel) { const int ke = geometri::topolojiOzeti(*d->brep).kenarSayi; for (int e = 1; e <= ke; ++e) loops.push_back(d->brep->kenarNoktalari((u32)e)); } else { loops = d->brep->yuzKonturlari(1); } for (const auto& lp : loops) { std::vector tepe, taban; for (const auto& pl : lp) { const Vec3d wp = d->donusum.noktaUygula(pl); const Vec3f p{(f32)wp.x, (f32)wp.y, (f32)wp.z}; ImVec2 sb, st; if (proj(p, sb) && proj(p + gz.normalD * gz.mesafe, st)) { taban.push_back(sb); tepe.push_back(st); } } const int n = (int)tepe.size(); for (int i = 0; i < n; ++i) { if (i + 1 < n || (!gz.tel && !gz.kenarMod)) // tel/kenar: açık zincir; yüzey: kapalı loop dl->AddLine(tepe[i], tepe[(i + 1) % n], col, 1.8f); dl->AddLine(taban[i], tepe[i], bag, 1.0f); } } } // Mesafe etiketi (mod'a göre) ImVec2 ml; if (proj(gz.merkezD + gz.normalD * gz.mesafe, ml)) { char b[56]; if (tipVar) std::snprintf(b, sizeof(b), "%s mm ▏", gz.tipBuf); else std::snprintf(b, sizeof(b), "%s %.2f mm", modAd, gz.mesafe); gizmo::degerEtiketi(dl, ml, b, modRenk); } // ESC = iptal · Enter/bırak = commit (parametrik yuzeyOffset feature). if (ImGui::IsKeyPressed(ImGuiKey_Escape)) { gz.surukluyor = false; gz.tipBuf[0] = 0; return; } if (ImGui::IsMouseReleased(ImGuiMouseButton_Left) || ImGui::IsKeyPressed(ImGuiKey_Enter)) { const bool enterIle = ImGui::IsKeyPressed(ImGuiKey_Enter); const f32 mes = gz.mesafe; const u32 kaynak = gz.dugum, kOrd = gz.kenarOrd; const bool uz = uzat, telHedef = gz.tel, kenarHedef = gz.kenarMod; const Vec3f nrm = gz.normalD; gz.surukluyor = false; gz.tipBuf[0] = 0; // ÇOK-NOKTA EDİTÖRÜ (2+ seçili): bırakış = aktif noktanın miktarını SAKLA (commit YOK — // başka nokta seçip onu da sürükleyebilirsin); Enter = hepsini kendi miktarıyla uygula. if (sisir && gz.sisirIdx.size() >= 2) { if (gz.sisirAktif >= 0 && gz.sisirAktif < (int)gz.sisirMik.size()) gz.sisirMik[gz.sisirAktif] = mes; if (enterIle) gsdSisirCokUygula(gz, sahne, cekirdek, gsdSis); return; } if (std::abs(mes) > 1e-3f) { const f32 s = mes < 0 ? -1.0f : 1.0f; // ters sürükleme → ters yön if (kenarHedef && gz.kenarKaydirMod) { // AYNI kenar tekrar mı? (seçili düğüm = kenarKaydir SONUCU ve yeni çıpa, eski kenarın // TAŞINMIŞ konumuna (kn+vec) yakın) → vec TOPLA, feature yığma. Farklı kenar → yeni. if (const u32 ef = gsdAyniTipFeature(gsdSis, kaynak, "kenarKaydir")) { if (gsd::GsdFeature* f = gsdSis.bul(ef)) { const f32 tx = (f32)(f->p("knx") + f->p("vx")) - gz.merkezD.x; const f32 ty = (f32)(f->p("kny") + f->p("vy")) - gz.merkezD.y; const f32 tz = (f32)(f->p("knz") + f->p("vz")) - gz.merkezD.z; f32 esik = 10.0f; if (Dugum* dn = sahne.bul(kaynak)) esik = std::max(dn->dunyaYaricap() * 0.35f, 1.0f); if (tx * tx + ty * ty + tz * tz <= esik * esik) { gsdSis.gsdGuncelleToplu(ef, {{"vx", f->p("vx") + (f64)(nrm.x * mes)}, {"vy", f->p("vy") + (f64)(nrm.y * mes)}, {"vz", f->p("vz") + (f64)(nrm.z * mes)}}); dmTutamacOlayiYay(cekirdek, "Yüzey", "Kenar Kaydır"); ui::toastGoster("Kenar kaydırma güncellendi", ui::ToastTip::Basari); return; } } } // Kenarı it-çek: sınır kenarını nrm×mes kadar taşı → yüzeyi pürüzsüz reshape (strip DEĞİL). const u32 fk = gsdSis.gsdBaslat("kenarKaydir", VARLIK_YOK, kaynak); if (fk) { // kenar noktası (yerel=dünya, identity) + yön×işaretli mesafe — TOPLU (tek insa). gsdSis.gsdGuncelleToplu(fk, {{"knx", (f64)gz.merkezD.x}, {"kny", (f64)gz.merkezD.y}, {"knz", (f64)gz.merkezD.z}, {"vx", (f64)(nrm.x * mes)}, {"vy", (f64)(nrm.y * mes)}, {"vz", (f64)(nrm.z * mes)}}); dmTutamacOlayiYay(cekirdek, "Yüzey", "Kenar Kaydır"); ui::toastGoster("Kenar kaydırıldı", ui::ToastTip::Basari); } else ui::toastGoster("Kenar kaydırma başarısız", ui::ToastTip::Hata); } else if (kenarHedef) { // Aynı kaynağın AYNI kenarı zaten uzatılmışsa → vektörel topla (strip yığılmasın). u32 ef = 0; if (dogrudanAyar().gsdTekFeature) for (u32 fk2 : gsdSis.featureler()) if (gsd::GsdFeature* f2 = gsdSis.bul(fk2); f2 && f2->kaynak() == kaynak && f2->tip() == "kenarExtrude" && (u32)(f2->p("kenarOrd") + 0.5) == kOrd) { ef = fk2; break; } if (ef) { gsd::GsdFeature* f = gsdSis.bul(ef); const f64 em = f->p("mesafe", 0.0); const f64 tx = f->p("yonX") * em + (f64)(nrm.x * mes); const f64 ty = f->p("yonY") * em + (f64)(nrm.y * mes); const f64 tz = f->p("yonZ") * em + (f64)(nrm.z * mes); const f64 tl = std::sqrt(tx * tx + ty * ty + tz * tz); if (tl > 1e-3) { gsdSis.gsdGuncelleToplu(ef, {{"yonX", tx / tl}, {"yonY", ty / tl}, {"yonZ", tz / tl}, {"mesafe", tl}}); dmTutamacOlayiYay(cekirdek, "Yüzey", "Kenar Uzat"); ui::toastGoster("Kenar uzatma güncellendi", ui::ToastTip::Basari); } else ui::toastGoster("Uzatma ~sıfır oldu — değer değişmedi", ui::ToastTip::Uyari); return; } // Tek kenarı yön (dışa-düzlem) boyunca uzat → strip yüzey. const u32 fk = gsdSis.gsdBaslat("kenarExtrude", VARLIK_YOK, kaynak); if (fk) { gsdSis.gsdGuncelleToplu(fk, {{"kenarOrd", (f64)kOrd}, {"yonX", (f64)(nrm.x * s)}, {"yonY", (f64)(nrm.y * s)}, {"yonZ", (f64)(nrm.z * s)}, {"mesafe", (f64)std::abs(mes)}}); dmTutamacOlayiYay(cekirdek, "Yüzey", "Kenar Uzat"); ui::toastGoster("Kenar uzatıldı", ui::ToastTip::Basari); } else ui::toastGoster("Kenar uzatma başarısız", ui::ToastTip::Hata); } else if (telHedef) { // Aynı tel zaten extrude edildiyse → vektörel topla (yüzey yığılmasın). if (const u32 ef = gsdKaynakliFeature(gsdSis, kaynak, "telExtrude")) { gsd::GsdFeature* f = gsdSis.bul(ef); const f64 em = f->p("mesafe", 0.0); const f64 tx = f->p("yonX") * em + (f64)(nrm.x * mes); const f64 ty = f->p("yonY") * em + (f64)(nrm.y * mes); const f64 tz = f->p("yonZ") * em + (f64)(nrm.z * mes); const f64 tl = std::sqrt(tx * tx + ty * ty + tz * tz); if (tl > 1e-3) { gsdSis.gsdGuncelleToplu(ef, {{"yonX", tx / tl}, {"yonY", ty / tl}, {"yonZ", tz / tl}, {"mesafe", tl}}); dmTutamacOlayiYay(cekirdek, "Yüzey", "Tel Extrude"); ui::toastGoster("Tel extrude güncellendi", ui::ToastTip::Basari); } else ui::toastGoster("Extrude ~sıfır oldu — değer değişmedi", ui::ToastTip::Uyari); return; } // Tel → yüzeye çek (extrude). Yön = normal × işaret; mesafe pozitif. const u32 fk = gsdSis.gsdBaslat("telExtrude", VARLIK_YOK, kaynak); if (fk) { gsdSis.gsdGuncelleToplu(fk, {{"yonX", (f64)(nrm.x * s)}, {"yonY", (f64)(nrm.y * s)}, {"yonZ", (f64)(nrm.z * s)}, {"mesafe", (f64)std::abs(mes)}}); dmTutamacOlayiYay(cekirdek, "Yüzey", "Tel Extrude"); ui::toastGoster("Tel yüzeye çekildi", ui::ToastTip::Basari); } else ui::toastGoster("Tel extrude başarısız", ui::ToastTip::Hata); } else if (sisir) { // Şişir/İndir: yüzey merkezinde (merkezD) NURBS kubbe; miktar = sürükleme (işaretli). // Etki yarıçapı YÜZEY BOYUNA orantılı (sabit değil) → küçük yüzeyde ofset değil kubbe olur. // Seçili düğüm şişir SONUCU ya da kaynağın şişir feature'ı zaten var (seçim eski // kaynakta kaldıysa) → yeni feature yığma, mevcuda hedef ekle. u32 ef = gsdAyniTipFeature(gsdSis, kaynak, "yuzeySisir"); if (!ef) ef = gsdKaynakliFeature(gsdSis, kaynak, "yuzeySisir"); if (ef) { std::vector> yeni; if (!gz.sisirIdx.empty()) for (int gi : gz.sisirIdx) if (gi >= 0 && gi < (int)gz.gridDunya.size()) yeni.push_back({gz.gridDunya[gi], mes}); if (yeni.empty()) yeni.push_back({gz.merkezD, mes}); gsdSisirHedefEkle(gsdSis, ef, yeni); gz.sisirIdx.clear(); gz.sisirMik.clear(); gz.sisirAktif = -1; gz.gridDugum = 0; gsdSonucuSec(gsdSis, sahne, ef); dmTutamacOlayiYay(cekirdek, "Yüzey", "Yüzey Şişir"); ui::toastGoster("Şişirme güncellendi", ui::ToastTip::Basari); return; } f32 yaricap = 20.0f; if (Dugum* dn = sahne.bul(kaynak)) yaricap = std::max(dn->dunyaYaricap() * dogrudanAyar().gsdSisirYaricapOran, 0.5f); const u32 fk = gsdSis.gsdBaslat("yuzeySisir", VARLIK_YOK, kaynak); if (fk) { // TOPLU ata → tek yeniden-insa (param başına rebuild = donma). std::vector> d{ {"yaricap", (f64)yaricap}, {"sinirKilit", dogrudanAyar().gsdSisirSinirKilit ? 1.0 : 0.0}, {"kilitDerinlik", (f64)dogrudanAyar().gsdSisirKilitDerinlik}}; if (!gz.sisirIdx.empty()) { // çok-hedef: her seçili nokta ayrı tepe (aynı miktar) d.push_back({"hedefSayi", (f64)gz.sisirIdx.size()}); for (size_t i = 0; i < gz.sisirIdx.size(); ++i) { const Vec3f& p = gz.gridDunya[gz.sisirIdx[i]]; const std::string k = std::to_string(i); d.push_back({"h" + k + "x", (f64)p.x}); d.push_back({"h" + k + "y", (f64)p.y}); d.push_back({"h" + k + "z", (f64)p.z}); d.push_back({"m" + k, (f64)mes}); } } else { // tek hedef (merkez / seçim yok) — eski yol d.push_back({"hedefX", (f64)gz.merkezD.x}); d.push_back({"hedefY", (f64)gz.merkezD.y}); d.push_back({"hedefZ", (f64)gz.merkezD.z}); d.push_back({"miktar", (f64)mes}); } gsdSis.gsdGuncelleToplu(fk, d); gz.sisirIdx.clear(); gz.sisirMik.clear(); gz.sisirAktif = -1; gz.gridDugum = 0; // yeni yüzey → seçim sıfır, grid tazelensin gsdSonucuSec(gsdSis, sahne, fk); // seçim sonuca → sonraki şişirme aynı feature dmTutamacOlayiYay(cekirdek, "Yüzey", "Yüzey Şişir"); ui::toastGoster(mes >= 0 ? "Yüzey şişirildi" : "Yüzey indirildi", ui::ToastTip::Basari); } else ui::toastGoster("Şişirme başarısız", ui::ToastTip::Hata); } else if (kalinlastir) { // Aynı yüzey zaten kalınlaştırıldıysa → kalınlığı topla (ikinci katı üretme). if (const u32 ef = gsdKaynakliFeature(gsdSis, kaynak, "yuzeyThicken")) { gsd::GsdFeature* f = gsdSis.bul(ef); gsdSis.gsdGuncelle(ef, "kalinlik", f->p("kalinlik", 0.0) + (f64)mes); dmTutamacOlayiYay(cekirdek, "Yüzey", "Yüzey Kalınlaştır"); ui::toastGoster("Kalınlık güncellendi", ui::ToastTip::Basari); return; } // Kalınlaştır: açık yüzeyi işaretli kalınlıkla KATIYA şişir (sürükleme yönü tarafı seçer). const u32 fk = gsdSis.gsdBaslat("yuzeyThicken", VARLIK_YOK, kaynak); if (fk) { gsdSis.gsdGuncelle(fk, "kalinlik", (f64)mes); dmTutamacOlayiYay(cekirdek, "Yüzey", "Yüzey Kalınlaştır"); ui::toastGoster("Yüzey kalınlaştırıldı", ui::ToastTip::Basari); } else ui::toastGoster("Kalınlaştırma başarısız", ui::ToastTip::Hata); } else { // Tüm yüzey: Ofset (işaretli) ya da Uzat (pozitif, 4 yön). const char* tip = uz ? "yuzeyExtrapolate" : "yuzeyOffset"; const char* ad = uz ? "Yüzey Uzat" : "Yüzey Offset"; const char* okMsg = uz ? "Yüzey uzatıldı" : "Offset yüzey eklendi"; // Seçili düğüm aynı-tip SONUÇ ya da kaynağın aynı-tip feature'ı var (seçim eski // kaynakta kaldıysa) → mesafeyi topla (feature yığma). u32 ef = gsdAyniTipFeature(gsdSis, kaynak, tip); if (!ef) ef = gsdKaynakliFeature(gsdSis, kaynak, tip); if (ef) { gsd::GsdFeature* f = gsdSis.bul(ef); const f64 eski = f ? f->p("mesafe", 0.0) : 0.0; gsdSis.gsdGuncelle(ef, "mesafe", eski + (f64)(uz ? std::abs(mes) : mes)); gsdSonucuSec(gsdSis, sahne, ef); dmTutamacOlayiYay(cekirdek, "Yüzey", ad); ui::toastGoster(uz ? "Uzatma güncellendi" : "Offset güncellendi", ui::ToastTip::Basari); return; } const u32 fk = gsdSis.gsdBaslat(tip, VARLIK_YOK, kaynak); if (fk) { gsdSis.gsdGuncelle(fk, "mesafe", (f64)(uz ? std::abs(mes) : mes)); gsdSonucuSec(gsdSis, sahne, fk); dmTutamacOlayiYay(cekirdek, "Yüzey", ad); ui::toastGoster(okMsg, ui::ToastTip::Basari); } else ui::toastGoster("İşlem başarısız", ui::ToastTip::Hata); } } } } } // namespace fiko