#pragma once // Kesit düzlemi 3D gizmosu (CATIA Dynamic Sectioning UX): her aktif kesit // düzlemi sahnede yarı saydam dikdörtgen + merkez ok tutamacı olarak görünür. // Ok tutamacından tutup NORMALİ boyunca sürükle → mesafe CANLI kayar (clip // push-constant kare-başı; B-Rep profil/kapak throttle'lı yeniden hesap). // Dikdörtgen kenarına/tutamaca ÇİFT TIK → ters çevir. Pusula (pusula.hpp) // overlay kalıbının birebir ikizi: ImDrawList + dünya→ekran projeksiyon. #include "fiko/analiz/analiz_sistemi.hpp" #include "pusula.hpp" namespace fiko { // Kesit gizmo ayarları (CLAUDE.md: sihirli sayı gömme yok). ayar_kayitlari.cpp // kaydetKesit kaydeder; gizmo bu global'i okur. struct KesitGizmoAyar { bool dolgu = false; // Kare içi yarı saydam dolgu (kapalı = yalnız çerçeve — // model dolgunun ardından net görünür; kullanıcı tercihi) bool profilTut = true; // Kesişim profilinden de tutup sürükle (oka nişan alma zorunlu değil) float profilTutPiksel = 7.0f; // Profil sürükleme yakalama toleransı (piksel) int profilTutMaksSegment = 4000; // Profil hover taramasında en çok segment (0 = sınırsız). // Dev modelde her-kare O(N) taramayı sınırlar. }; inline KesitGizmoAyar& kesitGizmoAyar() { static KesitGizmoAyar a; return a; } struct KesitGizmoDurum { u32 surukleKesit = 0; // sürüklenen KesitDuzlemi kimliği (0 = yok) f32 basMesafe = 0; // sürükleme başındaki mesafe f32 t0 = 0; // ışın-eksen parametresi (basışta) Vec3f eksen{}; // sürükleme ekseni (basıştaki normal, sabitlenir) Vec3f dayanak{}; // sürükleme merkez noktası (basışta sabitlenir) f64 sonAgirGuncelleme = 0; // profil/kapak throttle zamanı (saniye) }; // pencereAcikMi: gizmo YALNIZ kesit penceresi (tekil veya liste) açıkken çizilir — // kapatınca gizmo da kaybolur, clip/kesit görüntüsü sürer (kullanıcı isteği). Pasif // kesitin gizmosu soluk çizilir. inline void kesitGizmoCizVeIsle(analiz::AnalizSistemi& an, Sahne& sahne, OrbitKamera& kamera, ImDrawList* dl, ImGuiIO& io, bool vpHover, f32 mx, f32 my, f32 vw, f32 vh, f32 enBoy, KesitGizmoDurum& G, bool& handleUstunde, const std::function& pencereAcikMi) { using namespace pusula_ic; if (an.kesitler().empty()) return; // Hiçbir kesit penceresi açık değilse gizmo yok → tüm kapsam/projeksiyon işi de atlanır. if (!pencereAcikMi) return; bool herhangiAcik = false; for (u32 k : an.kesitler()) if (pencereAcikMi(k)) { herhangiAcik = true; break; } if (!herhangiAcik) return; const Mat4f vp = kamera.izdusum(enBoy) * kamera.gorunum(); const Mat4f invVP = vp.ters(); const ImVec2 fp = io.MousePos; const f32 ndcX = 2.0f * (fp.x - mx) / vw - 1.0f; const f32 ndcY = 2.0f * (fp.y - my) / vh - 1.0f; const Isin isin = imlecIsin(invVP, ndcX, ndcY); // Sahne kapsamı: görünür brep gövdelerin dünya küresi → dikdörtgen boyutu. Vec3f sahneMerkez{}; f32 sahneR = 0; { u32 say = 0; for (Dugum& d : sahne.dugumler()) { if (!d.brep || !d.brep->gecerliMi() || d.indeksSayi == 0) continue; if (!sahne.agacGorunur(d.kimlik) || !d.yerelSinir.gecerliMi()) continue; sahneMerkez = sahneMerkez + d.dunyaMerkez(); ++say; } if (say) sahneMerkez = sahneMerkez * (1.0f / (f32)say); for (Dugum& d : sahne.dugumler()) { if (!d.brep || !d.brep->gecerliMi() || d.indeksSayi == 0) continue; if (!sahne.agacGorunur(d.kimlik) || !d.yerelSinir.gecerliMi()) continue; const f32 r = (d.dunyaMerkez() - sahneMerkez).uzunluk() + d.dunyaYaricap(); sahneR = std::max(sahneR, r); } } if (sahneR <= 0) sahneR = std::max(kamera.mesafe() * 0.35f, 0.05f); const f32 h = sahneR * 1.12f; // dikdörtgen yarı-kenarı const auto dunyaNokta = [](const Dugum& d, const Vec3f& p) -> Vec3f { const Vec3d w = d.donusum.noktaUygula({(f64)p.x, (f64)p.y, (f64)p.z}); return {(f32)w.x, (f32)w.y, (f32)w.z}; }; for (u32 kimlik : an.kesitler()) { analiz::KesitDuzlemi* kd = an.kesitBul(kimlik); if (!kd) continue; if (!pencereAcikMi(kimlik)) continue; // penceresi kapalı → gizmo yok (kesit görüntüsü sürer) const bool pasif = !kd->aktif; const Vec3f n = kd->normal.uzunlukKare() > 1e-8f ? kd->normal.birim() : Vec3f{0, 0, 1}; // Klip normali (ters → -n); ok KALAN tarafı gösterir. const Vec3f klipN = kd->ters ? Vec3f{-n.x, -n.y, -n.z} : n; // Düzlem noktası; dikdörtgen merkezi = sahne merkezinin düzleme izdüşümü. const Vec3f pn = kd->nokta(); const Vec3f fark = sahneMerkez - pn; const Vec3f m = sahneMerkez - n * fark.nokta(n); Vec3f u, v; { const Vec3f ref = std::abs(n.z) < 0.9f ? Vec3f{0, 0, 1} : Vec3f{1, 0, 0}; u = n.capraz(ref).birim(); v = n.capraz(u); } const ImVec2 c0 = projekte(vp, m, mx, my, vw, vh); const ImVec2 kose[4] = {projekte(vp, m + u * h + v * h, mx, my, vw, vh), projekte(vp, m - u * h + v * h, mx, my, vw, vh), projekte(vp, m - u * h - v * h, mx, my, vw, vh), projekte(vp, m + u * h - v * h, mx, my, vw, vh)}; // Ok tutamacı: merkez → kalan taraf (klip normali) yönünde. const f32 okL = h * 0.22f; const ImVec2 okUc = projekte(vp, m + klipN * okL, mx, my, vw, vh); // ---- Hover testleri ---- const bool suruklenen = G.surukleKesit == kimlik; bool tutamacUst = false, kenarUst = false; if (vpHover && G.surukleKesit == 0) { tutamacUst = manhattan(fp, c0) < 14.0f || segmentUzaklik(fp, c0, okUc) < 9.0f || manhattan(fp, okUc) < 12.0f; if (!tutamacUst) for (int i = 0; i < 4 && !kenarUst; ++i) kenarUst = segmentUzaklik(fp, kose[i], kose[(i + 1) % 4]) < 7.0f; } // Kesişim profili tutamacı: küçük oka nişan almadan, kesitin model üstünde // çizdiği eğriden de tutup sürüklenebilir (kullanıcı isteği). Profil düğümü // dünya-uzayında saklanır (kenarNesneEkle; dönüşüm birim) ama yine de düğümün // matrisi uygulanır — düğüm ileride taşınırsa tutamaç kaymasın. bool profilUst = false; Dugum* pd = (kesitGizmoAyar().profilTut && kd->profilGoster && kd->profilDugum) ? sahne.bul(kd->profilDugum) : nullptr; if (pd && vpHover && G.surukleKesit == 0 && !tutamacUst && !kenarUst && !pd->cpuKenar.empty() && sahne.agacGorunurHizli(pd->kimlik)) { const f32 tol = kesitGizmoAyar().profilTutPiksel; // Sıcak yol koruması: önce düğüm sınır kutusunun EKRAN bbox'ı — imleç uzaktaysa // (olağan durum) segment taraması hiç koşmaz (8 projeksiyon vs binlerce). bool yakin = true; if (pd->yerelSinir.gecerliMi()) { const Aabbf& kt = pd->yerelSinir; f32 x0 = 1e30f, y0 = 1e30f, x1 = -1e30f, y1 = -1e30f; for (int i = 0; i < 8; ++i) { const Vec3f k{(i & 1) ? kt.azami.x : kt.asgari.x, (i & 2) ? kt.azami.y : kt.asgari.y, (i & 4) ? kt.azami.z : kt.asgari.z}; const ImVec2 s = projekte(vp, dunyaNokta(*pd, k), mx, my, vw, vh); x0 = std::min(x0, s.x); y0 = std::min(y0, s.y); x1 = std::max(x1, s.x); y1 = std::max(y1, s.y); } yakin = fp.x >= x0 - tol && fp.x <= x1 + tol && fp.y >= y0 - tol && fp.y <= y1 + tol; } if (yakin) { const size_t maks = kesitGizmoAyar().profilTutMaksSegment > 0 ? (size_t)kesitGizmoAyar().profilTutMaksSegment * 2 : pd->cpuKenar.size(); const size_t say = std::min(pd->cpuKenar.size(), maks); for (size_t i = 0; i + 1 < say && !profilUst; i += 2) { const ImVec2 a = projekte(vp, dunyaNokta(*pd, pd->cpuKenar[i]), mx, my, vw, vh); const ImVec2 b = projekte(vp, dunyaNokta(*pd, pd->cpuKenar[i + 1]), mx, my, vw, vh); profilUst = segmentUzaklik(fp, a, b) < tol; } } } if (tutamacUst || kenarUst || profilUst || suruklenen) handleUstunde = true; // ---- Çizim ---- const bool vurgulu = tutamacUst || kenarUst || profilUst || suruklenen; const ImU32 dolgu = pasif ? IM_COL32(140, 150, 160, 14) : vurgulu ? IM_COL32(60, 190, 235, 34) : IM_COL32(60, 190, 235, 20); const ImU32 cerceve = pasif ? IM_COL32(150, 160, 170, 90) : vurgulu ? IM_COL32(90, 215, 255, 235) : IM_COL32(70, 185, 225, 150); // Kare içi VARSAYILAN BOŞ: dolgu modeli soluklaştırıyordu (kullanıcı isteği); // yalnız çerçeve. Ayardan geri açılabilir (eski davranış). if (kesitGizmoAyar().dolgu) dl->AddQuadFilled(kose[0], kose[1], kose[2], kose[3], dolgu); dl->AddQuad(kose[0], kose[1], kose[2], kose[3], cerceve, vurgulu ? 2.4f : 1.4f); // Ok gövdesi + ucu (ekran-uzayı üçgen başlık). dl->AddLine(c0, okUc, cerceve, vurgulu ? 3.2f : 2.2f); { ImVec2 yon{okUc.x - c0.x, okUc.y - c0.y}; const f32 len = std::sqrt(yon.x * yon.x + yon.y * yon.y); if (len > 1e-3f) { yon.x /= len; yon.y /= len; const ImVec2 dik{-yon.y, yon.x}; const f32 b = vurgulu ? 9.0f : 7.0f; dl->AddTriangleFilled( ImVec2(okUc.x + yon.x * b * 1.6f, okUc.y + yon.y * b * 1.6f), ImVec2(okUc.x + dik.x * b * 0.6f, okUc.y + dik.y * b * 0.6f), ImVec2(okUc.x - dik.x * b * 0.6f, okUc.y - dik.y * b * 0.6f), cerceve); } } dl->AddCircleFilled(c0, vurgulu ? 7.0f : 5.5f, cerceve, 20); dl->AddCircle(c0, vurgulu ? 7.0f : 5.5f, IM_COL32(255, 255, 255, 190), 20, 1.4f); if (vurgulu && !suruklenen) dl->AddText(ImVec2(c0.x + 12, c0.y - 22), IM_COL32(200, 235, 250, 230), "sürükle: kaydır • çift tık: ters"); // ---- Etkileşim ---- if ((tutamacUst || kenarUst) && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { kd->ters = !kd->ters; an.kesitGuncelle(kimlik); continue; } if ((tutamacUst || profilUst) && G.surukleKesit == 0 && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { G.surukleKesit = kimlik; G.basMesafe = kd->mesafe; G.eksen = n; G.dayanak = m; G.t0 = isinEksenParam(isin, m, n); G.sonAgirGuncelleme = ImGui::GetTime(); } if (suruklenen) { if (io.MouseDown[ImGuiMouseButton_Left]) { const f32 t = isinEksenParam(isin, G.dayanak, G.eksen); kd->mesafe = G.basMesafe + (t - G.t0); // clip push-constant → ANLIK char b[48]; std::snprintf(b, sizeof(b), "Mesafe: %.2f mm", kd->mesafe); dl->AddText(ImVec2(fp.x + 14, fp.y - 8), IM_COL32(255, 220, 130, 255), b); // Ağır işler throttle'lı — sürükleme akıcı kalır. Varsayılanda yalnız // PROFİL güncellenir (kapak 17.9 ms vs profil 3.2 ms ölçüldü); OCCT // kapak bırakışta. kesit.surukle_kapak_canli açıksa eski davranış. const f64 simdi = ImGui::GetTime(); if (simdi - G.sonAgirGuncelleme > (f64)an.kesitSurukleMs / 1000.0) { G.sonAgirGuncelleme = simdi; an.kesitGuncelle(kimlik, an.kesitSurukleKapakCanli); } } if (ImGui::IsMouseReleased(ImGuiMouseButton_Left)) { G.surukleKesit = 0; an.kesitGuncelle(kimlik); // bırakışta kesin hesap } } } } } // namespace fiko