#include "sketch_ui.hpp" #include #include #include #include #include #include #include #include #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/sahne/sahne.hpp" #include "fiko/sahne/sketch_nesne.hpp" #include "fiko/sketch/eskiz_veri.hpp" #include "fiko/sketch/kisit.hpp" #include "fiko/sketch/eskiz_segment.hpp" #include "fiko/sketch/snap.hpp" #include "fiko/ui/bilesen.hpp" #include "fiko/ui/pencere_cerceve.hpp" #include "imgui.h" #include "imgui_internal.h" #include "pencere_util.hpp" #include "uygulama_baglam.hpp" namespace fiko { namespace { // Boyutsal (değerli) kısıt mı — bağlam menüsü + Animate Constraint paylaşır. inline bool kisitBoyutsalMi(eskiz::KisitTip t) { return t == eskiz::KisitTip::Mesafe || t == eskiz::KisitTip::YatayMesafe || t == eskiz::KisitTip::DikeyMesafe || t == eskiz::KisitTip::Yaricap || t == eskiz::KisitTip::Cap || t == eskiz::KisitTip::Aci; } // Araç kimliği → CATIA tarzı Türkçe "Tanım" penceresi başlığı (merkezi). // Sketch Tools penceresi aktif araca göre kendini adlandırır → "her aracın kendi penceresi". inline const char* sketchAracAdi(const std::string& a) { static const std::unordered_map harita = { {"cizgi", "Çizgi"}, {"sonsuzCizgi", "Sonsuz Çizgi"}, {"aciortay", "Açıortay"}, {"ikiTegetCizgi", "İki Teğet Çizgi"}, {"dikdortgen", "Dikdörtgen"}, {"merkezliDikdortgen", "Merkezli Dikdörtgen"}, {"yonluDikdortgen", "Yönlü Dikdörtgen"}, {"paralelkenar", "Paralelkenar"}, {"daire", "Daire"}, {"daireKoord", "Daire (Koordinat)"}, {"daire3", "Üç Nokta Daire"}, {"yay", "Yay"}, {"yay3", "Üç Nokta Yay"}, {"merkezYay", "Merkez Yay"}, {"elips", "Elips"}, {"elipsYay", "Eliptik Yay"}, {"slot", "Slot"}, {"egriselSlot", "Eğrisel Slot"}, {"anahtarDeligi", "Anahtar Deliği"}, {"cokgen", "Çokgen"}, {"nokta", "Nokta"}, {"noktaKoord", "Nokta (Koordinat)"}, {"kesisimNokta", "Kesişim Noktası"}, {"izdusumNokta", "İzdüşüm Noktası"}, {"trim", "Kırp"}, {"hizlikirp", "Hızlı Kırp"}, {"fillet", "Köşe (Fillet)"}, {"pah", "Pah (Chamfer)"}, {"ayna", "Ayna"}, {"simetri", "Simetri"}, {"otele", "Ötele"}, {"dondur", "Döndür"}, {"olcekle", "Ölçekle"}, {"ofset", "Ofset"}}; auto it = harita.find(a); return it != harita.end() ? it->second : a.c_str(); } } // namespace // ============================================================================ // Sketcher paneli (araç + kısıt + DOF durumu) — main.cpp'den birebir taşındı. // ============================================================================ void sketcherPaneliCiz(UygulamaBaglam& b) { auto& sketch = b.sketch; bool& panelSketch = sketch.panelSketch; bool& sketchUstte = sketch.sketchUstte; std::string& sketchArac = sketch.sketchArac; std::vector& sketchSecili = sketch.sketchSecili; bool& sketchGrid = sketch.sketchGrid; bool& sketchGridAdaptif = sketch.sketchGridAdaptif; bool& sketchGridSnap = sketch.sketchGridSnap; bool& sketchYapiMod = sketch.sketchYapiMod; bool& sketchOtoGeoKisit = sketch.sketchOtoGeoKisit; bool& sketchOtoOlcuKisit = sketch.sketchOtoOlcuKisit; bool& sketchAnalizAcik = sketch.sketchAnalizAcik; f32& sketchGridAralik = sketch.sketchGridAralik; int& sketchDuzenFaz = sketch.sketchDuzenFaz; Cekirdek& cekirdek = b.cekirdek; const f32 ekranG = ImGui::GetIO().DisplaySize.x; if (!panelSketch) return; // ---- MİNİ/KOMPAKT Sketcher paneli ---- // Araç + kısıt düğmeleri artık alt şeritte (kategorili flyout) + sahnede sağ-tık // bağlam menüsünde. Panel yalnız DURUM + Sketch Tools toggle'ları + çıkış tutar. // SABİT genişlik (AlwaysAutoResize DEĞİL): içerik available-width'e göre hizalanır, // auto-resize açık olsaydı pencere↔içerik geri besleme döngüsü → sürekli genişleme. ImGui::SetNextWindowSize({210, 0}, ImGuiCond_FirstUseEver); ImGui::SetNextWindowPos({ekranG - 226, 92}, ImGuiCond_FirstUseEver); ImGui::SetNextWindowBgAlpha(0.95f); ustteTutSinif(sketchUstte); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 6)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6, 4)); if (ImGui::Begin("Sketcher", &panelSketch, ImGuiWindowFlags_NoDocking)) { ustteTutDugmesi("sketch", sketchUstte); SketchNesnesi* skP = b.sketchNesne(); if (!skP) { ImGui::TextDisabled("Aktif sketch yok."); ImGui::TextWrapped("Düzlem/düz yüz seçip 'Sketch Oluştur' çalıştırın."); } else { eskiz::EskizVeri& ev = skP->eskiz(); // ---- Başlık + DOF rozeti (CATIA Sketch Solving Status renk kodu) ---- const int dof = ev.dof(); ImVec4 dr; const char* dm; switch (ev.durum()) { case eskiz::Solver::Durum::IyiKisitli: dr = {0.35f, 0.86f, 0.43f, 1.0f}; dm = "Tam belirli"; break; case eskiz::Solver::Durum::AzKisitli: dr = {0.55f, 0.72f, 0.98f, 1.0f}; dm = "Az belirli"; break; case eskiz::Solver::Durum::AsiriKisitli: dr = {0.78f, 0.43f, 0.94f, 1.0f}; dm = "Aşırı belirli"; break; case eskiz::Solver::Durum::Celiskili: dr = {0.94f, 0.35f, 0.35f, 1.0f}; dm = "Çelişkili"; break; default: dr = {0.94f, 0.35f, 0.35f, 1.0f}; dm = "Yakınsamadı"; break; } ImGui::TextColored(dr, "●"); ImGui::SameLine(0, 5); ImGui::TextUnformatted(skP->ad().c_str()); ImGui::TextColored(dr, "%s", dm); if (ev.durum() == eskiz::Solver::Durum::AzKisitli) { ImGui::SameLine(); ImGui::TextDisabled("(%d DOF)", dof); } ImGui::SameLine(ImGui::GetContentRegionAvail().x - 4, 0); ImGui::TextDisabled("%zu·%zu", ev.geoSayisi(), ev.kisitSayisi()); // Profil kapalılık (TEK OTORİTE = analiz()). { const auto an = ev.analiz(); if (ev.geometriler().empty()) ImGui::TextDisabled("Profil: boş"); else if (!an.acikUclar.empty()) ImGui::TextColored({0.94f, 0.70f, 0.30f, 1.0f}, "⚠ Açık profil (%d uç)", (int)an.acikUclar.size()); else ImGui::TextColored({0.35f, 0.86f, 0.55f, 1.0f}, "✔ Kapalı profil"); } // Aktif araç ipucu (kompakt tek satır). if (!sketchArac.empty()) { const bool duzen = b.sketchDuzenMi(sketchArac); if (!duzen) ImGui::TextColored({1.0f, 0.78f, 0.28f, 1.0f}, "▸ %s — düzleme tıkla", sketchArac.c_str()); else if (sketchArac == "fillet" || sketchArac == "pah") ImGui::TextColored({0.31f, 0.86f, 1.0f, 1.0f}, "▸ %s · %zu/2 çizgi", sketchArac.c_str(), sketchSecili.size()); else ImGui::TextColored({0.31f, 0.86f, 1.0f, 1.0f}, "▸ %s · %zu seçili · faz %d", sketchArac.c_str(), sketchSecili.size(), sketchDuzenFaz); } ImGui::Separator(); // ---- Sketch Tools toggle'ları (CATIA Sketch tools bar) — kompakt 2 sütun ---- ImGui::Checkbox("Izgara", &sketchGrid); ImGui::SameLine(104); ImGui::SetNextItemWidth(-1); ImGui::InputFloat("##ga", &sketchGridAralik, 0.0f, 0.0f, "%.2f"); if (sketchGridAralik < 0.01f) sketchGridAralik = 0.01f; ImGui::Checkbox("Adaptif", &sketchGridAdaptif); ImGui::SameLine(104); ImGui::Checkbox("Snap", &sketchGridSnap); ImGui::Checkbox("Yapı", &sketchYapiMod); ImGui::SameLine(104); ImGui::Checkbox("Analiz", &sketchAnalizAcik); ImGui::Checkbox("Oto Geo", &sketchOtoGeoKisit); ImGui::SameLine(104); ImGui::Checkbox("Oto Ölçü", &sketchOtoOlcuKisit); ImGui::Separator(); if (ImGui::Button("✔ Sketch'ten Çık", {-1, 0})) { for (const MenuOge& o : cekirdek.menuler().hepsi()) if (o.grup == "Sketch" && o.ad == "Sketch'ten Çık") { cekirdek.komutCalistir(o, "sketch"); break; } } } } ImGui::End(); ImGui::PopStyleVar(2); } // ============================================================================ // CATIA "Sketch Tools" SABİT toolbar + çizim/düzenleme tamamlama akışı. // ============================================================================ void sketchAraclariCiz(UygulamaBaglam& b) { auto& sketch = b.sketch; Varlik& aktifSketch = sketch.aktifSketch; std::string& sketchArac = sketch.sketchArac; std::vector>& sketchNoktalar = sketch.sketchNoktalar; std::vector& sketchSecili = sketch.sketchSecili; eskiz::SnapSonuc& sonSnap = sketch.sonSnap; bool& sketchGrid = sketch.sketchGrid; bool& sketchGridSnap = sketch.sketchGridSnap; bool& sketchYapiMod = sketch.sketchYapiMod; f32& sketchGridAralik = sketch.sketchGridAralik; int& sketchPolyKenar = sketch.sketchPolyKenar; char (&sketchGiris)[4][32] = sketch.sketchGiris; bool& sketchGirisAktif = sketch.sketchGirisAktif; int& sketchDuzenFaz = sketch.sketchDuzenFaz; std::array& sketchDuzenRef = sketch.sketchDuzenRef; char (&sketchDuzGiris)[4][32] = sketch.sketchDuzGiris; bool& sketchDuzGirisAktif = sketch.sketchDuzGirisAktif; Cekirdek& cekirdek = b.cekirdek; // ---- CATIA "Sketch Tools" SABİT toolbar ---- // Sketcher workbench aktifken 3D Görünüm üst kenarına yapışık yatay şerit. // Aktif araca (sketchArac) göre DİNAMİK ölçü alanları. Değerler eskisi gibi // sketchGiris[]/sketchDuzGiris[] dizilerine yazılır; çizim/düzenleme tamamlama // mantığı (mouse-yanı popup'tan taşındı) KORUNUR. Enter veya "Uygula" → değer aktif. sketchGirisAktif = false; sketchDuzGirisAktif = false; if (SketchNesnesi* skT = b.sketchNesne(); skT && cekirdek.workbenchler().aktif() == "sketcher") { eskiz::EskizVeri& ev = skT->eskiz(); const bool duzenModu = b.sketchDuzenMi(sketchArac); // 3D Görünüm üst kenarına snap'li konum (wbtb_ toolbarların altına). const f32 tvx0 = b.vpRectMinX, tvy0 = b.vpRectMinY; const bool tvpGecerli = b.istenenVpG >= 120 && b.istenenVpY >= 120 && tvx0 >= 1.0f; if (tvpGecerli) { ImGui::SetNextWindowBgAlpha(0.93f); ImGui::SetNextWindowPos({tvx0 + 8.0f, tvy0 + 44.0f}, ImGuiCond_FirstUseEver); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(6, 4)); ustteTutSinif(true); // ana pencereye owner → 3D Görünüm arkasına düşmez // CATIA: aktif araç varsa pencere onun "Tanım" diyaloğu gibi adlanır (sabit ###id). char sketchBaslik[96]; if (!sketchArac.empty()) std::snprintf(sketchBaslik, sizeof(sketchBaslik), "%s Tanımı###sketchtools", sketchAracAdi(sketchArac)); else std::snprintf(sketchBaslik, sizeof(sketchBaslik), "Sketch Tools###sketchtools"); if (ImGui::Begin(sketchBaslik, nullptr, ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoSavedSettings)) { // ⊾ Dik Bak: aktif sketch düzlemine dik (Normal View) — hep erişilebilir. if (aktifSketch == VARLIK_YOK) ImGui::BeginDisabled(); if (ui::ikonButon("⊾", "Kamerayı sketch düzlemine dik getir")) b.sketchDikBak(aktifSketch); if (aktifSketch == VARLIK_YOK) ImGui::EndDisabled(); ImGui::SameLine(0, 10); // CATIA Tanım diyaloğu: araç aktifken İptal (aracı bırak · ESC eşdeğeri). if (!sketchArac.empty()) { if (ui::ikonButon("✖", "Aracı bırak (ESC)")) b.sketchAracSec(""); ImGui::SameLine(0, 10); } const auto bayrak = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_CharsDecimal; // Dar etiketli alan (L: [___]) — yatay dizili. buf: giriş dizisi elemanı. const auto sayiAlan = [&](const char* et, char* buf, size_t n, const char* kim) -> bool { ImGui::TextUnformatted(et); ImGui::SameLine(0, 3); ImGui::SetNextItemWidth(70); const std::string id = std::string("##") + kim; const bool e = ImGui::InputText(id.c_str(), buf, n, bayrak); ImGui::SameLine(0, 8); return e; }; if (sketchArac.empty()) { // Boş/seçim modu: sketch ayarları (grid/snap/construction). ImGui::TextDisabled("Sketch Tools"); ImGui::SameLine(0, 10); ImGui::Checkbox("Grid", &sketchGrid); ImGui::SameLine(0, 8); ImGui::SetNextItemWidth(70); ImGui::InputFloat("Aralık", &sketchGridAralik, 0.0f, 0.0f, "%.2f"); ImGui::SameLine(0, 8); ImGui::Checkbox("Snap", &sketchGridSnap); ImGui::SameLine(0, 8); ImGui::Checkbox("Yapı", &sketchYapiMod); } else if (!duzenModu) { // ---- Çizim araçları: araca göre dinamik alanlar → sketchGiris[] ---- sketchGirisAktif = true; ImGui::TextDisabled("%s", sketchArac.c_str()); ImGui::SameLine(0, 10); bool enter = false; if (sketchArac == "cizgi") { enter |= sayiAlan("L:", sketchGiris[0], sizeof(sketchGiris[0]), "sgL"); enter |= sayiAlan("A°:", sketchGiris[1], sizeof(sketchGiris[1]), "sgA"); } else if (sketchArac == "dikdortgen" || sketchArac == "merkezliDikdortgen") { enter |= sayiAlan("En:", sketchGiris[0], sizeof(sketchGiris[0]), "sgEn"); enter |= sayiAlan("Boy:", sketchGiris[1], sizeof(sketchGiris[1]), "sgBoy"); } else if (sketchArac == "daire" || sketchArac == "daire3" || sketchArac == "yay" || sketchArac == "yay3") { enter |= sayiAlan("R:", sketchGiris[0], sizeof(sketchGiris[0]), "sgR"); } else if (sketchArac == "cokgen") { ImGui::TextUnformatted("Kenar:"); ImGui::SameLine(0, 3); ImGui::SetNextItemWidth(70); if (ImGui::InputInt("##sgKenar", &sketchPolyKenar)) sketchPolyKenar = std::clamp(sketchPolyKenar, 3, 20); ImGui::SameLine(0, 8); enter |= sayiAlan("R:", sketchGiris[0], sizeof(sketchGiris[0]), "sgR"); } else if (sketchArac == "elips") { enter |= sayiAlan("a:", sketchGiris[0], sizeof(sketchGiris[0]), "sgA1"); enter |= sayiAlan("b:", sketchGiris[1], sizeof(sketchGiris[1]), "sgB1"); } else if (sketchArac == "slot") { enter |= sayiAlan("L:", sketchGiris[0], sizeof(sketchGiris[0]), "sgSL"); enter |= sayiAlan("R:", sketchGiris[1], sizeof(sketchGiris[1]), "sgSR"); } else if (sketchArac == "daireKoord") { // CATIA Circle Using Coordinates: mutlak H/V merkez + yarıçap. enter |= sayiAlan("H:", sketchGiris[0], sizeof(sketchGiris[0]), "sgKH"); enter |= sayiAlan("V:", sketchGiris[1], sizeof(sketchGiris[1]), "sgKV"); enter |= sayiAlan("R:", sketchGiris[2], sizeof(sketchGiris[2]), "sgKR"); } else if (sketchArac == "noktaKoord") { // CATIA Point by Using Coordinates: mutlak H/V. enter |= sayiAlan("H:", sketchGiris[0], sizeof(sketchGiris[0]), "sgNH"); enter |= sayiAlan("V:", sketchGiris[1], sizeof(sketchGiris[1]), "sgNV"); } else if (sketchArac == "sonsuzCizgi") { // CATIA Infinite Line: Yatay/Dikey (1 tık) veya 2 Nokta modu. ImGui::SetNextItemWidth(96); const char* sonsuzEtiket[] = {"Yatay", "Dikey", "2 Nokta"}; if (ImGui::Combo("##sonsuzMod", &sketch.sonsuzMod, sonsuzEtiket, 3)) sketchNoktalar.clear(); // mod değişti → bekleyen tık geçersiz ImGui::SameLine(0, 8); } // Koord araçları: mutlak koordinatla doğrudan oluşturma (ön tık gerekmez). const bool koordArac = (sketchArac == "daireKoord" || sketchArac == "noktaKoord"); const bool ilkNoktaVar = !sketchNoktalar.empty(); const auto oku = [&](int i, f64 varsayilan) -> f64 { if (sketchGiris[i][0] == 0) return varsayilan; return std::strtod(sketchGiris[i], nullptr); }; // DEĞER→DRIVING KISIT (sketcher.oto_olcu_kisit): canlı girişe YAZILAN değer // son eklenen geometriye ölçü kısıtı olur (CATIA: değer girdin = ölçü koydun). // Fare/snap ölçüsüyle tamamlanan çizimde kısıt ÜRETİLMEZ (yalnız yazılan değer // niyettir — gürültü olmaz). Tamamla'dan HEMEN sonra çağrılır. // geriIdx: sondan kaç geometri geri (0=son; dikdörtgen kenarları için — Tamamla // sırası alt,sağ,üst,sol → alt=3 geri, sağ=2 geri). const auto otoOlcuKisit = [&](bool girildi, eskiz::KisitTip tip, f64 deger, std::size_t geriIdx = 0) { if (!sketch.sketchOtoOlcuKisit || !girildi || deger <= 1e-9) return; if (ev.geometriler().size() <= geriIdx) return; ev.kisitEkle(tip, {ev.geometriler()[ev.geometriler().size() - 1 - geriIdx].kimlik}, deger); ev.coz(); }; if (koordArac) { if (ImGui::Button("Oluştur")) enter = true; if (enter) { const f64 H = oku(0, 0.0), V = oku(1, 0.0); sketchNoktalar.clear(); if (sketchArac == "daireKoord") { const f64 R = oku(2, 0.0); if (R > 1e-6) { const bool rGirildi = sketchGiris[2][0] != 0; sketchNoktalar.push_back({H, V}); sketchNoktalar.push_back({H + R, V}); b.sketchAracTamamla(ev); otoOlcuKisit(rGirildi, eskiz::KisitTip::Yaricap, R); } else b.bilgiVer("Yarıçap girin"); } else { // noktaKoord sketchNoktalar.push_back({H, V}); b.sketchAracTamamla(ev); } sketchGiris[0][0] = sketchGiris[1][0] = sketchGiris[2][0] = 0; } } else { if (ilkNoktaVar) { if (ImGui::Button("Uygula")) enter = true; } else { ImGui::TextDisabled("(başlangıç için tıkla)"); } // Değer uygula: yalnız cizgi/dikdörtgen/daire için tam-ölçü tamamlama // (mevcut mantık; diğer araçlar fare/snap ile çizilir). if (enter && ilkNoktaVar) { const f64 ax = sketchNoktalar.back()[0], ay = sketchNoktalar.back()[1]; const f64 mu = sonSnap.tip != eskiz::SnapTip::Yok ? sonSnap.u : ax; const f64 mv = sonSnap.tip != eskiz::SnapTip::Yok ? sonSnap.v : ay; if (sketchArac == "cizgi") { const f64 hamL = std::hypot(mu - ax, mv - ay); const f64 hamA = std::atan2(mv - ay, mu - ax); const bool lGirildi = sketchGiris[0][0] != 0; const f64 L = oku(0, hamL) > 1e-6 ? oku(0, hamL) : 1.0; const f64 A = sketchGiris[1][0] ? oku(1, 0) * std::numbers::pi / 180.0 : hamA; sketchNoktalar.push_back({ax + std::cos(A) * L, ay + std::sin(A) * L}); b.sketchAracTamamla(ev); otoOlcuKisit(lGirildi, eskiz::KisitTip::Mesafe, L); } else if (sketchArac == "dikdortgen") { const bool enGirildi = sketchGiris[0][0] != 0; const bool boyGirildi = sketchGiris[1][0] != 0; const f64 sx = (mu - ax) >= 0 ? 1.0 : -1.0, sy = (mv - ay) >= 0 ? 1.0 : -1.0; const f64 en = oku(0, std::abs(mu - ax)); const f64 boy = oku(1, std::abs(mv - ay)); sketchNoktalar.push_back({ax + sx * en, ay + sy * boy}); b.sketchAracTamamla(ev); otoOlcuKisit(enGirildi, eskiz::KisitTip::Mesafe, en, 3); // alt kenar otoOlcuKisit(boyGirildi, eskiz::KisitTip::Mesafe, boy, 2); // sağ kenar } else if (sketchArac == "daire") { const bool rGirildi = sketchGiris[0][0] != 0; const f64 R = oku(0, std::hypot(mu - ax, mv - ay)); sketchNoktalar.push_back({ax + R, ay}); b.sketchAracTamamla(ev); otoOlcuKisit(rGirildi, eskiz::KisitTip::Yaricap, R); } sketchGiris[0][0] = sketchGiris[1][0] = 0; } } } else { // ---- Düzenleme modları: sketchDuzGiris[] → uygula ---- sketchDuzGirisAktif = true; ImGui::TextDisabled("%s", sketchArac.c_str()); ImGui::SameLine(0, 10); const bool aynaMod = (sketchArac == "ayna" || sketchArac == "simetri"); if (aynaMod) { // Taşı/Kopyala modu — ötele/döndür/ölçekle'deki Kopya alanının eşdeğeri. bool kop = sketch.sketchAynaKopya; if (ImGui::RadioButton("Kopyala", kop)) sketch.sketchAynaKopya = true; ImGui::SameLine(0, 6); if (ImGui::RadioButton("Taşı", !kop)) sketch.sketchAynaKopya = false; ImGui::SameLine(0, 10); } int alanSayi = 0; bool kopyaAlani = false; bool enter = false; const char* trimEtiket[] = {"Tümünü Kes", "Kesme", "İlkini Kes"}; if (sketchArac == "fillet") { enter |= sayiAlan("R:", sketchDuzGiris[0], sizeof(sketchDuzGiris[0]), "sdR"); alanSayi = 1; ImGui::SameLine(0, 10); ImGui::SetNextItemWidth(110); ImGui::Combo("##filletTrim", &sketch.filletTrimMod, trimEtiket, 3); } else if (sketchArac == "pah") { ImGui::SetNextItemWidth(130); const char* olcuEtiket[] = {"Açı+Hipotenüs", "1.+2. Uzunluk", "Açı+Uzunluk"}; ImGui::Combo("##pahOlcu", &sketch.pahOlcuMod, olcuEtiket, 3); ImGui::SameLine(0, 8); if (sketch.pahOlcuMod == 1) { enter |= sayiAlan("1.Uz:", sketchDuzGiris[0], sizeof(sketchDuzGiris[0]), "sdP1"); enter |= sayiAlan("2.Uz:", sketchDuzGiris[1], sizeof(sketchDuzGiris[1]), "sdP2"); } else if (sketch.pahOlcuMod == 2) { enter |= sayiAlan("1.Uz:", sketchDuzGiris[0], sizeof(sketchDuzGiris[0]), "sdP1"); enter |= sayiAlan("Açı°:", sketchDuzGiris[1], sizeof(sketchDuzGiris[1]), "sdPa"); } else { enter |= sayiAlan("Hip:", sketchDuzGiris[0], sizeof(sketchDuzGiris[0]), "sdPh"); enter |= sayiAlan("Açı°:", sketchDuzGiris[1], sizeof(sketchDuzGiris[1]), "sdPa"); } alanSayi = 2; ImGui::SameLine(0, 10); ImGui::SetNextItemWidth(110); ImGui::Combo("##pahTrim", &sketch.filletTrimMod, trimEtiket, 3); } else if (sketchArac == "hizlikirp") { // CATIA Quick Trim modu: Break and Rubber In / Rubber Out / Keep. ImGui::SetNextItemWidth(150); const char* kirpEtiket[] = {"İçini Sil", "Dışını Sil", "Sadece Böl"}; ImGui::Combo("##kirpMod", &sketch.kirpMod, kirpEtiket, 3); ImGui::SameLine(0, 10); ImGui::TextDisabled("parçaya tıkla"); } else if (sketchArac == "otele") { enter |= sayiAlan("dx:", sketchDuzGiris[0], sizeof(sketchDuzGiris[0]), "sdDx"); enter |= sayiAlan("dy:", sketchDuzGiris[1], sizeof(sketchDuzGiris[1]), "sdDy"); enter |= sayiAlan("Kopya:", sketchDuzGiris[2], sizeof(sketchDuzGiris[2]), "sdK"); alanSayi = 3; kopyaAlani = true; } else if (sketchArac == "dondur") { enter |= sayiAlan("Açı°:", sketchDuzGiris[0], sizeof(sketchDuzGiris[0]), "sdA"); enter |= sayiAlan("Kopya:", sketchDuzGiris[1], sizeof(sketchDuzGiris[1]), "sdK"); alanSayi = 2; kopyaAlani = true; } else if (sketchArac == "olcekle") { enter |= sayiAlan("Oran:", sketchDuzGiris[0], sizeof(sketchDuzGiris[0]), "sdO"); enter |= sayiAlan("Kopya:", sketchDuzGiris[1], sizeof(sketchDuzGiris[1]), "sdK"); alanSayi = 2; kopyaAlani = true; } else if (sketchArac == "ofset") { enter |= sayiAlan("Mesafe:", sketchDuzGiris[0], sizeof(sketchDuzGiris[0]), "sdOf"); alanSayi = 1; // CATIA Offset propagation: Yayılım (No/Tangent/Point) + Both Side. ImGui::SameLine(0, 10); ImGui::SetNextItemWidth(130); const char* yayilimEtiket[] = {"Yayılım: Yok", "Yayılım: Teğet", "Yayılım: Nokta"}; ImGui::Combo("##ofsetYayilim", &sketch.ofsetYayilim, yayilimEtiket, 3); ImGui::SameLine(0, 8); ImGui::Checkbox("Çift Taraf##ofsetCift", &sketch.ofsetCiftTaraf); } // Seç-seç-uygula araçları: değer alanı yok, yalnız yönlendirme + sayaç // (2. seçimde tık işleyicisi hemen uygular). const bool ikiliArac = sketchArac == "aciortay" || sketchArac == "tegetcizgi" || sketchArac == "kesisimnokta" || sketchArac == "izdusumnokta"; const bool hazir = sketchDuzenFaz == 1 && alanSayi > 0; if (ikiliArac) { const char* ipucu = sketchArac == "aciortay" ? "2 çizgi seç → açıortay (yapı)" : sketchArac == "tegetcizgi" ? "2 daire/yay seç → teğet çizgi" : sketchArac == "kesisimnokta" ? "2 geometri seç → kesişim noktası" : "1 nokta + 1 çizgi/daire seç → izdüşüm"; ImGui::TextDisabled("%s · %zu/2", ipucu, sketchSecili.size()); } else if (hazir) { if (ImGui::Button("Uygula")) enter = true; } else if (sketchDuzenFaz == 0 && !sketchSecili.empty()) { // Klavye (Enter) beklemeden faz ilerlet: seçim yapıldıysa "Devam". if (ImGui::Button("Devam ▶")) b.sketchDuzenIlerlet(); } else if (sketchDuzenFaz == 0) { ImGui::TextDisabled(aynaMod ? "(yansıtılacak eleman(lar)ı seç)" : "(önce eleman seç)"); } else if (aynaMod) { ImGui::TextDisabled("eksen çizgisini tıkla"); } const auto oku = [&](int i, f64 vs) -> f64 { return sketchDuzGiris[i][0] == 0 ? vs : std::strtod(sketchDuzGiris[i], nullptr); }; const int kopya = kopyaAlani ? std::max(0, (int)oku(alanSayi - 1, 0)) : 0; if (enter && hazir) { if (sketchArac == "fillet" && sketchSecili.size() >= 2) { ev.koseYuvarlat(sketchSecili[0], sketchSecili[1], oku(0, 1.0), eskiz::trimModDon(sketch.filletTrimMod)); } else if (sketchArac == "pah" && sketchSecili.size() >= 2) { const f64 v1yed = sketch.pahOlcuMod == 1 ? 1.0 : 45.0; // 2.Uz veya açı yedeği f64 mA, mB; pahMesafeHesapla(sketch.pahOlcuMod, oku(0, 1.0), oku(1, v1yed), mA, mB); ev.pah(sketchSecili[0], sketchSecili[1], mA, mB, eskiz::trimModDon(sketch.filletTrimMod)); } else if (sketchArac == "otele") { const f64 dx = oku(0, 0.0), dy = oku(1, 0.0); ev.otele(sketchSecili, dx, dy, kopya); } else if (sketchArac == "dondur") { const f64 aci = oku(0, 90.0) * std::numbers::pi / 180.0; ev.dondur(sketchSecili, sketchDuzenRef[0], sketchDuzenRef[1], aci, kopya); } else if (sketchArac == "olcekle") { ev.olcekle(sketchSecili, sketchDuzenRef[0], sketchDuzenRef[1], oku(0, 2.0), kopya); } else if (sketchArac == "ofset") { // Yayılım: uygulamadan önce seçimi zincir komşularıyla genişlet // (1=Teğet süreklilik şartlı, 2=Nokta sürekliliği yeterli). if (sketch.ofsetYayilim > 0) { std::vector ek; for (u32 g : sketchSecili) for (u32 k : ev.zincirKomsulari(g, sketch.ofsetYayilim == 1)) if (std::find(sketchSecili.begin(), sketchSecili.end(), k) == sketchSecili.end() && std::find(ek.begin(), ek.end(), k) == ek.end()) ek.push_back(k); sketchSecili.insert(sketchSecili.end(), ek.begin(), ek.end()); } ev.ofsetle(sketchSecili, oku(0, 1.0), sketch.ofsetCiftTaraf); } ev.coz(); for (auto& s : sketchDuzGiris) s[0] = 0; sketchSecili.clear(); sketchDuzenFaz = 0; sketchArac.clear(); b.bilgiVer("Düzenleme uygulandı"); } } } ImGui::End(); ImGui::PopStyleVar(2); } } if (!sketchGirisAktif) { sketchGiris[0][0] = sketchGiris[1][0] = sketchGiris[2][0] = 0; } if (!sketchDuzGirisAktif) { for (auto& s : sketchDuzGiris) s[0] = 0; } } // ============================================================================ // Sketch overlay (eskiz geometri + seçim + snap + önizleme) — main.cpp'den birebir. // Viewport çizim döngüsünde (ölçüm/kütük overlay ile aynı ciz/ekrana bağlamında) // çağrılır; SketchCizimBaglam kare-yerel çizim durumunu taşır. // ============================================================================ void sketchOverlayCiz(UygulamaBaglam& b, const SketchCizimBaglam& cb) { auto& sketch = b.sketch; Varlik& aktifSketch = sketch.aktifSketch; std::vector>& sketchNoktalar = sketch.sketchNoktalar; std::vector& sketchSecili = sketch.sketchSecili; std::vector& sketchSeciliKisit = sketch.sketchSeciliKisit; auto& kisitHitKutulari = sketch.kisitHitKutulari; kisitHitKutulari.clear(); // her kare yeniden doldurulur (tık hit-test okur) eskiz::SnapMotoru& snapMotoru = sketch.snapMotoru; eskiz::SnapSonuc& sonSnap = sketch.sonSnap; bool& sketchGrid = sketch.sketchGrid; bool& sketchGridAdaptif = sketch.sketchGridAdaptif; bool& sketchGridSnap = sketch.sketchGridSnap; bool& sketchAnalizAcik = sketch.sketchAnalizAcik; f32& sketchGridAralik = sketch.sketchGridAralik; int& sketchPolyKenar = sketch.sketchPolyKenar; std::string& sketchArac = sketch.sketchArac; u32& sketchHover = sketch.sketchHover; const u32 disHoverGeo = sketch.disHoverGeo; // ürün ağacından hover (ağaç→sahne) const u32 disHoverKisit = sketch.disHoverKisit; char (&sketchDuzGiris)[4][32] = sketch.sketchDuzGiris; Cekirdek& cekirdek = b.cekirdek; Sahne& sahne = b.sahne; ImDrawList* ciz = cb.ciz; ImGuiIO& io = *cb.io; const bool vpHover = cb.vpHover; const f32 vpRectMinX = cb.vpRectMinX, vpRectMinY = cb.vpRectMinY; const auto& ekrana = cb.ekrana; const auto& uvDunya = cb.uvDunya; const auto& ekranUV = cb.ekranUV; const auto& sketchEnYakin = cb.sketchEnYakin; // ---- Sketch overlay (eskiz geometrileri + seçim + çizim önizleme) ---- // Düzlem-yerel uv → dünya → ekran. Ana renk DOF durumundan; yapı geometrisi // gri kesikli, seçili turuncu, çizim önizlemesi rubber-band. // TÜM sketch düğümleri üzerinde dön: pasif sketch'ler soluk çizilir (çıkınca // kaybolmaz), yalnız aktif olan snap/kısıt-sembol/önizleme/canlı geri bildirim alır. // CATIA: "aktif düzenleme" = Sketcher workbench + o sketch aktif. Toolbar'dan // başka workbench'e geçilirse (SketchCikildi çıkmadan) sembol/ölçü gizlenir. const bool sketcherAktif = cekirdek.workbenchler().aktif() == "sketcher"; for (auto& sd : sahne.dugumler()) { if (sd.tip != DugumTip::Sketch || !sahne.agacGorunur(sd.kimlik)) continue; auto* sk = dynamic_cast(cekirdek.nesne(sd.kimlik)); if (!sk) continue; if (Dugum* dz = sahne.bul(sk->duzlem())) { const bool aktifMi = (sd.kimlik == aktifSketch) && sketcherAktif; eskiz::EskizVeri& ev = sk->eskiz(); // ---- Selection Stack segment kopyası (Alt+tık) — geometri/düzlem değişince yenile ---- // cpuKenar/cpuKenarAlt Sahne::sketchKenarYenile ile dolar → sketch SEGMENT SEGMENT // seçilir. Perf: her kare değil, yalnız imza değişince rebuild (geo+kısıt+param+düzlem). { u64 sig = 1469598103934665603ull; const auto kat = [&](u64 x) { sig = (sig ^ x) * 1099511628211ull; }; kat(ev.geoSayisi() * 131u + ev.kisitSayisi()); for (f64 pv : ev.parametreler()) { u64 b; std::memcpy(&b, &pv, 8); kat(b); } for (f64 dv : {dz->donusum.konum.x, dz->donusum.konum.y, dz->donusum.konum.z, dz->donusum.yonelim.x, dz->donusum.yonelim.y, dz->donusum.yonelim.z, dz->donusum.yonelim.w}) { u64 b; std::memcpy(&b, &dv, 8); kat(b); } u64& son = sketch.kenarImza[sd.kimlik]; if (son != sig) { sahne.sketchKenarYenile(sd.kimlik); son = sig; } } ImU32 anaRenk; if (!aktifMi) { anaRenk = IM_COL32(120, 130, 150, 170); // pasif sketch soluk } else switch (ev.durum()) { case eskiz::Solver::Durum::IyiKisitli: anaRenk = IM_COL32(90, 220, 110, 255); break; case eskiz::Solver::Durum::AzKisitli: anaRenk = IM_COL32(235, 235, 240, 255); break; case eskiz::Solver::Durum::AsiriKisitli: anaRenk = IM_COL32(200, 110, 240, 255); break; default: anaRenk = IM_COL32(240, 90, 90, 255); break; } const ImU32 yapiRenk = aktifMi ? IM_COL32(136, 144, 168, 220) : IM_COL32(110, 118, 138, 150); // CATIA per-eleman "tam kısıtlı = yeşil": eskiz AzKisitli iken bile yeri kilitli // elemanlar yeşile döner (global renk yalnız IyiKisitli'de yeşildi). Kare başına // önbellek — çözüm imzası değişmedikçe geoTamKisitliMi taraması tekrarlanmaz. const bool tamKisitAktif = aktifMi && sketch.sketchTamKisitRenk && ev.durum() == eskiz::Solver::Durum::AzKisitli; if (tamKisitAktif) { const auto& spSerbest = ev.sonSonuc().serbestParamlar; u64 sig = 1469598103934665603ull; const auto kat = [&](u64 v) { sig = (sig ^ v) * 1099511628211ull; }; kat(ev.geometriler().size()); kat(ev.kisitlar().size()); kat((u64)(u32)ev.dof()); for (int ixS : spSerbest) kat((u64)(u32)ixS); if (sig != sketch.tamKisitImza) { sketch.tamKisitImza = sig; sketch.tamKisitOnb.clear(); for (const eskiz::Geometri& gg : ev.geometriler()) if (ev.geoTamKisitliMi(gg.kimlik)) sketch.tamKisitOnb.insert(gg.kimlik); } } const ImU32 seciliRenk = IM_COL32(255, 167, 38, 255); const ImU32 hoverRenk = IM_COL32(80, 220, 255, 255); // imleç-altı (cyan) const ImU32 noktaRenk = IM_COL32(245, 220, 120, 255); const ImU32 snapRenk = IM_COL32(255, 167, 38, 255); const auto ekUV = [&](f64 u, f64 v, ImVec2& s) -> bool { return ekrana(uvDunya(dz, u, v), s); }; const auto secilenMi = [&](u32 kimlik) { return std::find(sketchSecili.begin(), sketchSecili.end(), kimlik) != sketchSecili.end(); }; // Kesikli çizgi (construction / inference için) — segment segment atla. const auto kesikliCiz = [&](ImVec2 a, ImVec2 b, ImU32 renk, f32 kalin) { const f32 dx = b.x - a.x, dy = b.y - a.y; const f32 L = std::sqrt(dx * dx + dy * dy); if (L < 1e-3f) return; const f32 adim = 7.0f; const int n = (int)(L / adim); for (int i = 0; i < n; i += 2) { const f32 t0 = i * adim / L, t1 = std::min((i + 1) * adim / L, 1.0f); ciz->AddLine({a.x + dx * t0, a.y + dy * t0}, {a.x + dx * t1, a.y + dy * t1}, renk, kalin); } }; // Aperture/grid/snap çözümü YALNIZ aktif sketch için (pasif sketch soluk // çizilir, etkileşim almaz). Geometri çizimi aşağıda her sketch için döner. if (aktifMi) { // ---- Aperture: 11px ekran toleransını sketch birimine çevir ---- // uvDunya(0,0) ve uvDunya(1,0) ekrana projekte → 1 birimin ekran-px'i. ImVec2 o0, o1; if (ekUV(0, 0, o0) && ekUV(1, 0, o1)) { const f32 pxBirim = std::sqrt((o1.x - o0.x) * (o1.x - o0.x) + (o1.y - o0.y) * (o1.y - o0.y)); snapMotoru.aperture = pxBirim > 1e-3f ? 11.0 / (f64)pxBirim : 0.4; } // ---- Görünür alan + EFEKTİF grid adımı (snap VE grid çizimi ORTAK kullanır) ---- // 3D VIEWPORT köşelerini (io.DisplaySize DEĞİL — o tüm ekran) düzlem-yerel uv'ye çevir // → gerçek görünür alan. Adaptif modda adım 1/2/5·10^k (~28 çizgi) → zoom ne olursa // ekranı KAPLAR. Snap adımı = grid adımı → snap görünen ızgaraya tam oturur. f64 gridAdimEf = std::max(1e-3, (f64)sketchGridAralik); f64 vUMin = 0, vUMax = 0, vVMin = 0, vVMax = 0; bool vpUvGecerli = false; { const f32 vw = cb.vpBoyutX, vh = cb.vpBoyutY; if (vw > 1.0f && vh > 1.0f) { f64 uMin = 1e30, uMax = -1e30, vMin = 1e30, vMax = -1e30; int gecerli = 0; const f32 lxs[4] = {0.0f, vw, 0.0f, vw}; // viewport-YEREL köşeler (ekranUV yerel bekler) const f32 lys[4] = {0.0f, 0.0f, vh, vh}; for (int k = 0; k < 4; ++k) { f64 cu, cv; if (ekranUV(dz, lxs[k], lys[k], cu, cv)) { uMin = std::min(uMin, cu); uMax = std::max(uMax, cu); vMin = std::min(vMin, cv); vMax = std::max(vMax, cv); ++gecerli; } } const f64 gen = std::max(uMax - uMin, vMax - vMin); if (gecerli >= 3 && gen > 1e-9 && gen < 1e12) { vUMin = uMin; vUMax = uMax; vVMin = vMin; vVMax = vMax; vpUvGecerli = true; if (sketchGridAdaptif) { const f64 ham = gen / 28.0; const f64 us = std::pow(10.0, std::floor(std::log10(ham))); const f64 m = ham / us; gridAdimEf = (m < 2.0 ? 1.0 : m < 5.0 ? 2.0 : 5.0) * us; } } } } snapMotoru.gridAdim = gridAdimEf; // snap görünen ızgaraya oturur snapMotoru.izgaraSnap = sketchGridSnap; // ---- İzgara (grid) çizimi — görünür alanı adaptif adımla doldurur ---- if (sketchGrid && vpUvGecerli) { const ImU32 gridRenk = IM_COL32(88, 94, 110, 80); // minor const ImU32 gridMajor = IM_COL32(110, 118, 140, 120); // her 5. const ImU32 gridEksen = IM_COL32(122, 130, 152, 165); // u=0/v=0 const f64 adim = gridAdimEf; const int iMin = (int)std::floor(vUMin / adim), iMax = (int)std::ceil(vUMax / adim); const int jMin = (int)std::floor(vVMin / adim), jMax = (int)std::ceil(vVMax / adim); // DONMA GÜVENLİĞİ: aşırı çizgi (edge-case) → çizme (kilitlenme önlenir). if (iMax >= iMin && jMax >= jMin && (iMax - iMin) <= 600 && (jMax - jMin) <= 600) { ImVec2 p0, p1; // (b adı UygulamaBaglam parametresini gölgeliyordu — C4457) for (int i = iMin; i <= iMax; ++i) { const f64 c = i * adim; const bool maj = (i % 5 == 0); const ImU32 rk = (i == 0) ? gridEksen : (maj ? gridMajor : gridRenk); if (ekUV(c, jMin * adim, p0) && ekUV(c, jMax * adim, p1)) ciz->AddLine(p0, p1, rk, maj ? 1.3f : 1.0f); } for (int j = jMin; j <= jMax; ++j) { const f64 c = j * adim; const bool maj = (j % 5 == 0); const ImU32 rk = (j == 0) ? gridEksen : (maj ? gridMajor : gridRenk); if (ekUV(iMin * adim, c, p0) && ekUV(iMax * adim, c, p1)) ciz->AddLine(p0, p1, rk, maj ? 1.3f : 1.0f); } } } // ---- SmartPick snap çözümü (marker + snaplı önizleme için) ---- // Çözülemezse tip=Yok sentinel (tık işleyici ham fareye düşer). sonSnap = eskiz::SnapSonuc{}; sonSnap.tip = eskiz::SnapTip::Yok; sketchHover = 0; if (vpHover) { f64 hamU, hamV; const f32 mlx = io.MousePos.x - vpRectMinX, mly = io.MousePos.y - vpRectMinY; if (ekranUV(dz, mlx, mly, hamU, hamV)) { if (!sketchNoktalar.empty()) { const f64 au = sketchNoktalar.back()[0], av = sketchNoktalar.back()[1]; sonSnap = snapMotoru.coz(ev, hamU, hamV, &au, &av); } else { sonSnap = snapMotoru.coz(ev, hamU, hamV); } // CATIA: SHIFT basılıyken snap/inference geçici kapatılır (istenmeyen // geometrik şart engellenir) → ham imleç konumu kullanılır. if (io.KeyShift) { sonSnap.tip = eskiz::SnapTip::Serbest; sonSnap.u = hamU; sonSnap.v = hamV; sonSnap.yatayHiza = sonSnap.dikeyHiza = false; } // Düzenleme/seçim modunda imleç-altı geometri (cyan hover geri bildirim). if (sketchArac.empty() || b.sketchDuzenMi(sketchArac)) sketchHover = sketchEnYakin(ev, hamU, hamV); } } } for (const eskiz::Geometri& g : ev.geometriler()) { // Sketch Analizi "Yapı Geometrisini Gizle" (aktif sketch, yapı elemanı atla). if (aktifMi && g.yapi && sketch.sketchYapiGizle) continue; const bool sec = aktifMi && secilenMi(g.kimlik); const bool hov = aktifMi && !sec && (g.kimlik == sketchHover || g.kimlik == disHoverGeo); const bool tam = tamKisitAktif && !sec && !hov && !g.yapi && sketch.tamKisitOnb.count(g.kimlik) != 0; const ImU32 renk = sec ? seciliRenk : hov ? hoverRenk : tam ? IM_COL32(90, 220, 110, 255) // IyiKisitli tonu : (g.yapi ? yapiRenk : anaRenk); const f32 kalin = (sec || hov) ? 2.6f : 1.6f; if (g.tip == eskiz::GeoTip::Cizgi) { ImVec2 p0, p1; // (b → C4457 gölgeleme) if (ekUV(ev.noktaX(g.kimlik, 0), ev.noktaY(g.kimlik, 0), p0) && ekUV(ev.noktaX(g.kimlik, 1), ev.noktaY(g.kimlik, 1), p1)) { if (g.yapi) kesikliCiz(p0, p1, renk, kalin); else ciz->AddLine(p0, p1, renk, kalin); // Uç tutamaç noktaları yalnız aktif sketch'te (düzenleme affordance). if (aktifMi) { ciz->AddCircleFilled(p0, 3.0f, noktaRenk); ciz->AddCircleFilled(p1, 3.0f, noktaRenk); } } } else if (g.tip == eskiz::GeoTip::Daire || g.tip == eskiz::GeoTip::Yay) { const f64 cx = ev.noktaX(g.kimlik, 2), cy = ev.noktaY(g.kimlik, 2); const f64 r = ev.px(g.ir); // Yay ise açı aralığı ib1..ib2 (aksi halde yay tam çember gibi çizilirdi). f64 a0 = 0.0, a1 = 2.0 * std::numbers::pi; if (g.tip == eskiz::GeoTip::Yay && g.ib1 >= 0 && g.ib2 >= 0) { a0 = ev.px(g.ib1); a1 = ev.px(g.ib2); } ImVec2 onceki; bool oncekiVar = false; constexpr int seg = 40; for (int i = 0; i <= seg; ++i) { const f64 a = a0 + (a1 - a0) * (f64)i / (f64)seg; ImVec2 s; if (!ekUV(cx + r * std::cos(a), cy + r * std::sin(a), s)) { oncekiVar = false; continue; } if (oncekiVar) ciz->AddLine(onceki, s, renk, kalin); onceki = s; oncekiVar = true; } ImVec2 cm; // merkez tutamacı yalnız aktif sketch'te. if (aktifMi && ekUV(cx, cy, cm)) ciz->AddCircleFilled(cm, 3.0f, noktaRenk); } else if (g.tip == eskiz::GeoTip::Spline) { std::vector segler; segler.reserve(128); eskiz::geoSegmentle(ev, g, segler); for (const auto& seg : segler) { ImVec2 p0, p1; if (ekUV(seg.a.x, seg.a.y, p0) && ekUV(seg.b.x, seg.b.y, p1)) ciz->AddLine(p0, p1, renk, kalin); } if (aktifMi) { ImVec2 ua, ub; if (g.a.gecerli() && ekUV(ev.noktaX(g.kimlik, 0), ev.noktaY(g.kimlik, 0), ua)) ciz->AddCircleFilled(ua, 3.0f, noktaRenk); if (g.b.gecerli() && ekUV(ev.noktaX(g.kimlik, 1), ev.noktaY(g.kimlik, 1), ub)) ciz->AddCircleFilled(ub, 3.0f, noktaRenk); } } else { // Nokta (gerçek geometri — pasifte de soluk çizilir) ImVec2 s; if (ekUV(ev.noktaX(g.kimlik, 0), ev.noktaY(g.kimlik, 0), s)) { ciz->AddCircleFilled(s, 3.5f, sec ? seciliRenk : (aktifMi ? noktaRenk : renk)); ciz->AddCircle(s, 6.0f, renk, 0, 1.4f); } } } // ---- Kısıt sembolleri + boyutsal ölçü metinleri (CATIA tarzı) ---- // Geometrik kısıtlar geometrinin yakınında küçük yeşil glyph; boyutsal // kısıtlar (Mesafe/Yarıçap/Çap/Açı) değer + birim metni. Aynı geometriye // düşen semboller kısıt indexine göre kaydırılır (üst üste binmez). // CATIA: kısıt sembolü + ölçü YALNIZ aktif sketch düzenlenirken görünür; // sketch'ten çıkınca pasif sketch yalnız soluk geometri çizgisi kalır. // Sketch Analizi "Kısıtları Gizle" toggle'ı sembolleri tamamen gizler. // ---- NURBS kontrol noktaları + çokgeni gizmo (aktif sketch'te) ---- if (aktifMi && !sketch.sketchYapiGizle) { const ImU32 cpHov = IM_COL32(120, 220, 255, 240); // cyan hover const ImU32 cpSel = IM_COL32(255, 160, 80, 255); // orange selected const ImU32 cpDef = IM_COL32(180, 190, 210, 200); // gray default const ImU32 cpPoly = IM_COL32(80, 80, 100, 140); // control polygon for (const auto& g : ev.geometriler()) { if (g.tip != eskiz::GeoTip::Spline) continue; bool isNurbs = !g.knot.empty() && !g.weight.empty(); // Eski Spline (Catmull-Rom): sadece uçlar if (!isNurbs) { ImVec2 p0, p1; if (ekUV(ev.noktaX(g.kimlik, 0), ev.noktaY(g.kimlik, 0), p0)) ciz->AddCircleFilled(p0, 3.0f, cpDef); if (ekUV(ev.noktaX(g.kimlik, 1), ev.noktaY(g.kimlik, 1), p1)) ciz->AddCircleFilled(p1, 3.0f, cpDef); } else { // NURBS: kontrol noktaları + çokgeni çizimi int n = (int)g.weight.size(); std::vector> pts; pts.reserve((size_t)n); pts.emplace_back(ev.noktaX(g.kimlik, 0), ev.noktaY(g.kimlik, 0)); for (int i = 1; i < n-1 && i-1 < (int)g.ekNoktalar.size(); ++i) { const auto& ek = g.ekNoktalar[(size_t)(i-1)]; pts.emplace_back(ev.px(ek.ix), ev.px(ek.iy)); } if (n > 0 && n-2 < (int)g.ekNoktalar.size()) { const auto& ek = g.ekNoktalar[(size_t)(n-2)]; pts.emplace_back(ev.px(ek.ix), ev.px(ek.iy)); } else if (g.b.gecerli()) { pts.emplace_back(ev.noktaX(g.kimlik, 1), ev.noktaY(g.kimlik, 1)); } // Kontrol çokgeni (control polygon) - ince çizgi ImVec2 prev; bool first = true; for (const auto& p : pts) { ImVec2 s; if (ekUV(p.first, p.second, s)) { if (!first) ciz->AddLine(prev, s, cpPoly, 0.8f); prev = s; first = false; } } // Kontrol noktaları - boş daire + iç dolu küçük daire for (int i = 0; i < (int)pts.size(); ++i) { ImVec2 s; if (ekUV(pts[(size_t)i].first, pts[(size_t)i].second, s)) { ImU32 renk = cpDef; bool hovHere = g.kimlik == sketchHover; if (hovHere) renk = cpHov; if (sketch.sketchSurukleGeo == g.kimlik) renk = cpSel; // şu an sürükleniyor ciz->AddCircle(s, 4.0f, renk, 0, 1.5f); // dış halka ciz->AddCircleFilled(s, 2.0f, renk); // iç dolu } } } } } // ---- Spline/NURBS kontrol noktası tut-sürükle (onaylanmış eğriyi düzenleme) ---- // Yalnız araç boşken (seçim modu) aktif — çizim sırasında tık yeni nokta ekler, // burada karışmaz. noktaTasi() ham param değerini basar, ardından coz() ile // kısıtlar/bağımlı geometriler yeniden hesaplanır (CATIA "drag to satisfy" UX'i). if (aktifMi && sketchArac.empty()) { constexpr float hitR = 8.0f; if (sketch.sketchSurukleGeo != 0) { if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) { f64 u, v; // ÖNEMLİ: ekranUV viewport-YEREL piksel bekler (belge_penceresi.cpp'deki // lx/ly = io.MousePos - vpRectMin ile aynı). Ham io.MousePos geçmek, // viewport'un ekrandaki konumu kadar bir KAYMA yaratıyordu — nokta // imlecin tam altında değil, viewport ofseti kadar "başka yerde" duruyordu. if (ekranUV(dz, io.MousePos.x - vpRectMinX, io.MousePos.y - vpRectMinY, u, v)) { const eskiz::Geometri* g = ev.geoBul(sketch.sketchSurukleGeo); int ix = -1, iy = -1; if (g) { if (sketch.sketchSurukleIndex == -2 && g->a.gecerli()) { ix = g->a.ix; iy = g->a.iy; } else if (sketch.sketchSurukleIndex == -1 && g->b.gecerli()) { ix = g->b.ix; iy = g->b.iy; } else if (sketch.sketchSurukleIndex >= 0 && sketch.sketchSurukleIndex < (int)g->ekNoktalar.size()) { ix = g->ekNoktalar[(size_t)sketch.sketchSurukleIndex].ix; iy = g->ekNoktalar[(size_t)sketch.sketchSurukleIndex].iy; } } if (ix >= 0 && iy >= 0) { ev.noktaTasi(ix, iy, u, v); ev.coz(); } } } else { // Bırakıldı — belge_penceresi.cpp bu kare "sketchSurukleTuttu"yu okuyup // normal geometri/kısıt seçim toggle'ını atlayacak (drag ≠ seçim tıkı). sketch.sketchSurukleGeo = 0; sketch.sketchSurukleIndex = -100; sketch.sketchSurukleTuttu = true; } } else if (vpHover && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { float enYakinD2 = hitR * hitR; u32 enYakinGeo = 0; int enYakinIdx = -100; for (const auto& g : ev.geometriler()) { if (g.tip != eskiz::GeoTip::Spline) continue; const auto dene = [&](f64 px, f64 py, int idx) { ImVec2 s; if (!ekUV(px, py, s)) return; const float dx = s.x - io.MousePos.x, dy = s.y - io.MousePos.y; const float d2 = dx * dx + dy * dy; if (d2 < enYakinD2) { enYakinD2 = d2; enYakinGeo = g.kimlik; enYakinIdx = idx; } }; if (g.a.gecerli()) dene(ev.px(g.a.ix), ev.px(g.a.iy), -2); if (g.b.gecerli()) dene(ev.px(g.b.ix), ev.px(g.b.iy), -1); for (int i = 0; i < (int)g.ekNoktalar.size(); ++i) dene(ev.px(g.ekNoktalar[(size_t)i].ix), ev.px(g.ekNoktalar[(size_t)i].iy), i); } if (enYakinGeo != 0) { sketch.sketchSurukleGeo = enYakinGeo; sketch.sketchSurukleIndex = enYakinIdx; } } } if (aktifMi && !sketch.sketchKisitGizle) { const ImU32 kisitRenk = IM_COL32(52, 211, 153, 255); // driving yeşil const ImU32 refRenkK = IM_COL32(120, 200, 235, 235); // reference mavi const ImU32 olcuRenk = IM_COL32(255, 214, 120, 255); // boyut sarısı // geo kimliğinden ekran-uzayı "çapa" noktası (çizgi ortası / merkez / nokta). const auto geoCapa = [&](u32 kimlik, ImVec2& s) -> bool { const eskiz::Geometri* g = ev.geoBul(kimlik); if (!g) return false; if (g->tip == eskiz::GeoTip::Cizgi) return ekUV((ev.noktaX(kimlik, 0) + ev.noktaX(kimlik, 1)) * 0.5, (ev.noktaY(kimlik, 0) + ev.noktaY(kimlik, 1)) * 0.5, s); if (g->tip == eskiz::GeoTip::Daire || g->tip == eskiz::GeoTip::Yay) return ekUV(ev.noktaX(kimlik, 2), ev.noktaY(kimlik, 2), s); return ekUV(ev.noktaX(kimlik, 0), ev.noktaY(kimlik, 0), s); }; // Aynı geo'ya kaç sembol düştüğünü sayıp kaydırma ofseti üret. std::unordered_map capaSay; // Simgeyi çiz + ekran-uzayı hit kutusunu kaydet (tık ile seçilebilsin); // seçiliyse turuncu kutu ile vurgula. gKimlik döngüde her kısıta set edilir. u32 gKimlik = 0; const auto glyph = [&](const char* sim, ImVec2 s, ImU32 renk) { const ImVec2 p{s.x + 5, s.y - 6}; const ImVec2 ts = ImGui::CalcTextSize(sim); const bool secli = gKimlik == disHoverKisit || std::find(sketchSeciliKisit.begin(), sketchSeciliKisit.end(), gKimlik) != sketchSeciliKisit.end(); const ImVec2 k0{p.x - 2, p.y - 1}, k1{p.x + ts.x + 2, p.y + ts.y + 1}; if (secli) { ciz->AddRectFilled(k0, k1, IM_COL32(255, 140, 60, 70), 3.0f); ciz->AddRect(k0, k1, IM_COL32(255, 160, 80, 235), 3.0f); } ciz->AddText(p, secli ? IM_COL32(255, 190, 120, 255) : renk, sim); kisitHitKutulari.push_back({gKimlik, k0.x, k0.y, k1.x, k1.y}); }; for (const eskiz::Kisit& k : ev.kisitlar()) { if (k.geo.empty()) continue; ImVec2 s; if (!geoCapa(k.geo[0], s)) continue; gKimlik = k.kimlik; const int idx = capaSay[k.geo[0]]++; s.x += 12.0f * (f32)(idx % 3); // yatay diz s.y -= 12.0f * (f32)(idx / 3); // taşarsa üst sıra const ImU32 rk = k.suru ? kisitRenk : refRenkK; switch (k.tip) { case eskiz::KisitTip::Yatay: glyph("H", s, rk); break; case eskiz::KisitTip::Dikey: glyph("V", s, rk); break; case eskiz::KisitTip::Paralel: glyph("∥", s, rk); break; case eskiz::KisitTip::Dik: glyph("⊥", s, rk); break; case eskiz::KisitTip::Tegel: glyph("T", s, rk); break; case eskiz::KisitTip::Esit: glyph("=", s, rk); break; case eskiz::KisitTip::Cakisik: glyph("◇", s, rk); break; case eskiz::KisitTip::NoktaUzerinde:glyph("○", s, rk); break; case eskiz::KisitTip::Simetrik: glyph("↔", s, rk); break; case eskiz::KisitTip::Sabit: glyph("✕", s, rk); break; case eskiz::KisitTip::Mesafe: case eskiz::KisitTip::YatayMesafe: case eskiz::KisitTip::DikeyMesafe: { char buf[48]; std::snprintf(buf, sizeof(buf), "%.2f", k.deger); glyph(buf, s, k.suru ? olcuRenk : refRenkK); break; } case eskiz::KisitTip::Yaricap: { char buf[48]; std::snprintf(buf, sizeof(buf), "R%.2f", k.deger); glyph(buf, s, k.suru ? olcuRenk : refRenkK); break; } case eskiz::KisitTip::Cap: { char buf[48]; std::snprintf(buf, sizeof(buf), "Ø%.2f", k.deger); glyph(buf, s, k.suru ? olcuRenk : refRenkK); break; } case eskiz::KisitTip::Aci: { char buf[48]; std::snprintf(buf, sizeof(buf), "%.1f°", k.deger * 180.0 / std::numbers::pi); glyph(buf, s, k.suru ? olcuRenk : refRenkK); break; } } } } // ---- Çizim/düzenleme önizleme (rubber-band, CATIA canlı geri bildirim) ---- // Her çok-adımlı araçta: yeterli tık girildikten sonra, son tık YAPILMADAN // imlecin anlık (snaplı) konumuna göre sonuç geometrisi yarı-saydam çizilir. // Yardımcı: uv-uzayında yay/çember polyline (merkez, yarıçap, açı aralığı). const ImU32 onRenk = IM_COL32(120, 220, 255, 210); const auto onYay = [&](f64 cx, f64 cy, f64 r, f64 a0, f64 a1) { if (r < 1e-9) return; constexpr int seg = 48; ImVec2 onceki; bool oncekiVar = false; for (int i = 0; i <= seg; ++i) { const f64 a = a0 + (a1 - a0) * (f64)i / (f64)seg; ImVec2 s; if (!ekUV(cx + r * std::cos(a), cy + r * std::sin(a), s)) { oncekiVar = false; continue; } if (oncekiVar) ciz->AddLine(onceki, s, onRenk, 1.4f); onceki = s; oncekiVar = true; } }; const auto onCizgi = [&](f64 x0, f64 y0, f64 x1, f64 y1) { ImVec2 p0, p1; // (b → C4457 gölgeleme) if (ekUV(x0, y0, p0) && ekUV(x1, y1, p1)) ciz->AddLine(p0, p1, onRenk, 1.4f); }; const auto onNokta = [&](f64 x, f64 y) { ImVec2 s; if (ekUV(x, y, s)) ciz->AddCircle(s, 5.0f, onRenk, 12, 1.6f); }; // (1) Çizim araçları: biriken tıklar + canlı imleç ucundan önizleme. if (aktifMi && !sketchArac.empty() && !b.sketchDuzenMi(sketchArac) && !sketchNoktalar.empty() && vpHover) { const f64 mu = sonSnap.u, mv = sonSnap.v; // SmartPick snaplı nokta const auto& np = sketchNoktalar; const auto& n0 = np.back(); if (sketchArac == "daire" || sketchArac == "daireKoord") { onYay(n0[0], n0[1], std::hypot(mu - n0[0], mv - n0[1]), 0.0, 2.0 * std::numbers::pi); } else if (sketchArac == "merkezYay") { // 1 tık: merkez→imleç yarıçap çizgisi. 2 tık: merkez+başlangıç sabit, // imleç bitiş açısını sürer (CCW süpürme, sketchAracTamamla ile aynı). const f64 cx = np[0][0], cy = np[0][1]; if (np.size() == 1) { onCizgi(cx, cy, mu, mv); } else { // np.size()==2 const f64 r = std::hypot(np[1][0]-cx, np[1][1]-cy); const f64 a0 = std::atan2(np[1][1]-cy, np[1][0]-cx); f64 a1 = std::atan2(mv-cy, mu-cx); constexpr f64 tau = 2.0*std::numbers::pi; while (a1 <= a0) a1 += tau; onYay(cx, cy, r, a0, a1); } for (const auto& pt : sketchNoktalar) onNokta(pt[0], pt[1]); } else if (sketchArac == "dikdortgen") { onCizgi(n0[0], n0[1], mu, n0[1]); onCizgi(mu, n0[1], mu, mv); onCizgi(mu, mv, n0[0], mv); onCizgi(n0[0], mv, n0[0], n0[1]); } else if (sketchArac == "merkezliDikdortgen") { const f64 dx = std::abs(mu - n0[0]), dy = std::abs(mv - n0[1]); onCizgi(n0[0]-dx, n0[1]-dy, n0[0]+dx, n0[1]-dy); onCizgi(n0[0]+dx, n0[1]-dy, n0[0]+dx, n0[1]+dy); onCizgi(n0[0]+dx, n0[1]+dy, n0[0]-dx, n0[1]+dy); onCizgi(n0[0]-dx, n0[1]+dy, n0[0]-dx, n0[1]-dy); } else if (sketchArac == "cokgen") { const f64 r = std::hypot(mu - n0[0], mv - n0[1]); const int kn = std::clamp(sketchPolyKenar, 3, 20); const f64 a0 = std::atan2(mv - n0[1], mu - n0[0]); for (int i = 0; i < kn; ++i) { const f64 ai = a0 + 2.0 * std::numbers::pi * i / kn; const f64 aj = a0 + 2.0 * std::numbers::pi * (i + 1) / kn; onCizgi(n0[0] + r*std::cos(ai), n0[1] + r*std::sin(ai), n0[0] + r*std::cos(aj), n0[1] + r*std::sin(aj)); } } else if (sketchArac == "daire3" || sketchArac == "yay3") { // 1 tık: imlece çizgi. 2 tık: 3. noktayla geçen çember/yay canlı. if (np.size() == 1) { onCizgi(np[0][0], np[0][1], mu, mv); onNokta(np[0][0], np[0][1]); } else { // np.size()==2 → (p0,p1,imleç) çemberi const f64 ax=np[0][0], ay=np[0][1], bx=np[1][0], by=np[1][1], cx=mu, cy=mv; const f64 d = 2.0*(ax*(by-cy)+bx*(cy-ay)+cx*(ay-by)); if (std::abs(d) > 1e-9) { const f64 a2=ax*ax+ay*ay, b2=bx*bx+by*by, c2=cx*cx+cy*cy; const f64 mx=(a2*(by-cy)+b2*(cy-ay)+c2*(ay-by))/d; const f64 my=(a2*(cx-bx)+b2*(ax-cx)+c2*(bx-ax))/d; const f64 r=std::hypot(ax-mx, ay-my); if (sketchArac == "daire3") onYay(mx, my, r, 0.0, 2.0*std::numbers::pi); else { // yay3: p0→p1 arası, imleç yönünde f64 A0=std::atan2(ay-my,ax-mx), A1=std::atan2(by-my,bx-mx); const f64 Am=std::atan2(cy-my,cx-mx); auto nrm=[](f64 a){ constexpr f64 t=2.0*std::numbers::pi; while(a<0)a+=t; while(a>=t)a-=t; return a; }; if (nrm(Am-A0) > nrm(A1-A0)) std::swap(A0, A1); onYay(mx, my, r, A0, A0 + nrm(A1-A0)); } } else onCizgi(np[1][0], np[1][1], mu, mv); } for (const auto& pt : sketchNoktalar) onNokta(pt[0], pt[1]); } else if (sketchArac == "elips") { if (np.size() == 1) onCizgi(np[0][0], np[0][1], mu, mv); else { // np[0]=merkez, np[1]=büyük eksen ucu, imleç=küçük yarıçap const f64 cx=np[0][0], cy=np[0][1]; const f64 ex=np[1][0]-cx, ey=np[1][1]-cy, a=std::hypot(ex,ey), aci=std::atan2(ey,ex); const f64 pu=mu-cx, pv=mv-cy; const f64 kb=std::abs(-pu*std::sin(aci)+pv*std::cos(aci)); // küçük yarıçap (b→C4457) if (a > 1e-6 && kb > 1e-6) { constexpr int seg=64; ImVec2 onc; bool ov=false; for (int i=0;i<=seg;++i){ const f64 tt=2.0*std::numbers::pi*i/seg; const f64 lx=a*std::cos(tt), ly=kb*std::sin(tt); ImVec2 s; if(!ekUV(cx+lx*std::cos(aci)-ly*std::sin(aci), cy+lx*std::sin(aci)+ly*std::cos(aci), s)){ov=false;continue;} if(ov) ciz->AddLine(onc,s,onRenk,1.4f); onc=s; ov=true; } } } for (const auto& pt : sketchNoktalar) onNokta(pt[0], pt[1]); } else if (sketchArac == "slot") { if (np.size() < 2) onCizgi(np[0][0], np[0][1], mu, mv); else { // np[0],np[1]=eksen uçları, imleç=yarıçap const f64 x0=np[0][0], y0=np[0][1], x1=np[1][0], y1=np[1][1]; const f64 r=std::hypot(mu-x1, mv-y1); const f64 dx=x1-x0, dy=y1-y0, L=std::hypot(dx,dy); if (L > 1e-6 && r > 1e-6) { const f64 nx=-dy/L*r, ny=dx/L*r; // sol normal * r onCizgi(x0+nx, y0+ny, x1+nx, y1+ny); onCizgi(x0-nx, y0-ny, x1-nx, y1-ny); const f64 aEk=std::atan2(dy,dx); onYay(x1, y1, r, aEk - std::numbers::pi/2, aEk + std::numbers::pi/2); onYay(x0, y0, r, aEk + std::numbers::pi/2, aEk + 3*std::numbers::pi/2); } } for (const auto& pt : sketchNoktalar) onNokta(pt[0], pt[1]); } else if (sketchArac == "egriselSlot") { // 1 tık: merkez→imleç (eksen yarıçapı). 2 tık: eksen yayı canlı (imleç // bitiş açısını sürer). 3 tık: tam kontur (imleç genişliği sürer). const f64 cx = np[0][0], cy = np[0][1]; if (np.size() == 1) onCizgi(cx, cy, mu, mv); else { const f64 r = std::hypot(np[1][0]-cx, np[1][1]-cy); const f64 a0 = std::atan2(np[1][1]-cy, np[1][0]-cx); constexpr f64 tau = 2.0*std::numbers::pi; if (r > 1e-6) { if (np.size() == 2) { // eksen yayı önizleme f64 a1 = std::atan2(mv-cy, mu-cx); while (a1 <= a0) a1 += tau; onYay(cx, cy, r, a0, a1); } else { // np.size()==3 → imleç = genişlik noktası f64 a1 = std::atan2(np[2][1]-cy, np[2][0]-cx); while (a1 <= a0) a1 += tau; const f64 g = std::abs(std::hypot(mu-cx, mv-cy) - r); if (g > 1e-6 && g < r) { onYay(cx, cy, r+g, a0, a1); onYay(cx, cy, r-g, a0, a1); const f64 p0x = cx + r*std::cos(a0), p0y = cy + r*std::sin(a0); const f64 p1x = cx + r*std::cos(a1), p1y = cy + r*std::sin(a1); onYay(p0x, p0y, g, a0 - std::numbers::pi, a0); onYay(p1x, p1y, g, a1, a1 + std::numbers::pi); } else onYay(cx, cy, r, a0, a1); } } } for (const auto& pt : sketchNoktalar) onNokta(pt[0], pt[1]); } else if (sketchArac == "anahtarDeligi") { // 1 tık: büyük merkez→imleç. 2 tık: imleç küçük yarıçapı sürer → // tam anahtar deliği konturu (R = 2r) canlı. if (np.size() == 1) onCizgi(np[0][0], np[0][1], mu, mv); else { // np[0]=büyük merkez, np[1]=küçük merkez, imleç=küçük r const f64 bx = np[0][0], by = np[0][1], kx = np[1][0], ky = np[1][1]; const f64 r = std::hypot(mu-kx, mv-ky), R = 2.0*r; const f64 dx = kx-bx, dy = ky-by, d = std::hypot(dx, dy); if (r > 1e-6 && d > R - r + 1e-6) { const f64 alfa = std::atan2(dy, dx); const f64 beta = std::acos(std::clamp((R - r) / d, -1.0, 1.0)); constexpr f64 tau = 2.0*std::numbers::pi; onYay(bx, by, R, alfa+beta, alfa-beta+tau); // büyük yay (uzak taraf) onYay(kx, ky, r, alfa-beta, alfa+beta); // küçük yay (uzak taraf) for (const f64 isaret : {1.0, -1.0}) { const f64 aa = alfa + isaret*beta; onCizgi(bx + R*std::cos(aa), by + R*std::sin(aa), kx + r*std::cos(aa), ky + r*std::sin(aa)); } } else if (r > 1e-6) { // iç içe → iki çember göster (geçersiz ipucu) onYay(bx, by, R, 0.0, 2.0*std::numbers::pi); onYay(kx, ky, r, 0.0, 2.0*std::numbers::pi); } } for (const auto& pt : sketchNoktalar) onNokta(pt[0], pt[1]); } else if (sketchArac == "yonluDikdortgen") { if (np.size() == 1) onCizgi(np[0][0], np[0][1], mu, mv); // taban doğrultusu else { // np[0],np[1]=taban, imleç=yükseklik const f64 x0=np[0][0], y0=np[0][1], x1=np[1][0], y1=np[1][1]; const f64 dx=x1-x0, dy=y1-y0, L=std::hypot(dx,dy); if (L > 1e-6) { const f64 nx=-dy/L, ny=dx/L; // sol birim normal const f64 h=(mu-x0)*nx + (mv-y0)*ny; // işaretli yükseklik onCizgi(x0, y0, x1, y1); onCizgi(x1, y1, x1+nx*h, y1+ny*h); onCizgi(x1+nx*h, y1+ny*h, x0+nx*h, y0+ny*h); onCizgi(x0+nx*h, y0+ny*h, x0, y0); } } for (const auto& pt : sketchNoktalar) onNokta(pt[0], pt[1]); } else if (sketchArac == "paralelkenar") { if (np.size() == 1) onCizgi(np[0][0], np[0][1], mu, mv); // 1. kenar else { // np[0]=köşe, np[1]=1.kenar ucu, imleç=2.kenar ucu const f64 x0=np[0][0], y0=np[0][1]; const f64 v1x=np[1][0]-x0, v1y=np[1][1]-y0; const f64 v2x=mu-x0, v2y=mv-y0; onCizgi(x0, y0, x0+v1x, y0+v1y); onCizgi(x0+v1x, y0+v1y, x0+v1x+v2x, y0+v1y+v2y); onCizgi(x0+v1x+v2x, y0+v1y+v2y, x0+v2x, y0+v2y); onCizgi(x0+v2x, y0+v2y, x0, y0); } for (const auto& pt : sketchNoktalar) onNokta(pt[0], pt[1]); } else if (sketchArac == "sonsuzCizgi") { // 2 Nokta modu: 1. tık + imleç yönünde ±1e4 uzun (yapı) çizgi. const f64 dx = mu - n0[0], dy = mv - n0[1], L = std::hypot(dx, dy); if (L > 1e-9) onCizgi(n0[0] - dx / L * 1e4, n0[1] - dy / L * 1e4, n0[0] + dx / L * 1e4, n0[1] + dy / L * 1e4); onNokta(n0[0], n0[1]); } else if (sketchArac != "spline" && sketchArac != "nurbsEgri") { // cizgi + genel yedek: son nokta → imleç onCizgi(n0[0], n0[1], mu, mv); } } // Spline / NURBS Egri cizim onizleme: birikmis kontrol noktaları + canlı eğri. // İkisi de aynı yerden besleniyor; NURBS eskiden ayrı, spline hiç önizleme // almıyordu (spline sadece sağ-tık/çift-tık sonrası oluşan geometriyle // "aniden" görünüyordu) — artık ikisi de canlı çiziyor. // ÖNEMLİ: eğri/çokgen SADECE yerleşmiş (tıklanmış) noktalardan kurulur — bu, // sağ-tık ile Tamamla'nın ürettiği geometriyle BİREBİR aynı olmalı. İmleç ucu // eskiden eğriye "hayalet" nokta olarak dahil ediliyordu; bu, sağ-tıkla // bitirince (imleç konumu asla noktaya dönüşmediği için) eğrinin son // segmentinin aniden kaybolması / son 1-2 noktanın "iptal olmuş" gibi // görünmesine yol açıyordu. İmleç artık yalnız kesikli bir "sıradaki nokta" // ipucu olarak ayrı çizilir, eğri hesâbına karışmaz. if (aktifMi && (sketchArac == "spline" || sketchArac == "nurbsEgri") && !sketchNoktalar.empty() && vpHover) { const ImU32 cpPre = IM_COL32(100, 200, 255, 220); const ImU32 cpImlec = IM_COL32(255, 190, 90, 230); // imleç ucu = turuncu (henüz yerleşmedi) // Kontrol noktalarını çiz (yerleşmiş) for (const auto& pt : sketchNoktalar) { ImVec2 s; if (ekUV(pt[0], pt[1], s)) { ciz->AddCircle(s, 5.0f, cpPre, 12, 1.6f); ciz->AddCircleFilled(s, 2.0f, cpPre); } } // Kontrol çokgeni (yalnız yerleşmiş noktalar — Tamamla ile birebir eşleşir) if (sketchNoktalar.size() >= 2) { ImVec2 prev; bool first = true; for (const auto& pt : sketchNoktalar) { ImVec2 s; if (ekUV(pt[0], pt[1], s)) { if (!first) ciz->AddLine(prev, s, cpPre, 1.0f); prev = s; first = false; } } } // Gerçek eğriyi çiz (kontrol noktalarından geçmez, kontrol çokgeninden geçer). // Yalnız yerleşmiş noktalar → sağ-tık/çift-tık Tamamla ile TAM aynı sonuç. if (sketchNoktalar.size() >= 3) { std::vector> ctrlPts; ctrlPts.reserve(sketchNoktalar.size()); for (const auto& pt : sketchNoktalar) ctrlPts.push_back({pt[0], pt[1]}); const int n = (int)ctrlPts.size(); ImVec2 onceki; bool oncekiVar = false; constexpr int stepsPerSeg = 8; for (int i = 0; i <= n - 2; ++i) { const f64* p0 = (i >= 1) ? &ctrlPts[(size_t)(i-1)][0] : &ctrlPts[0][0]; const f64* p1 = &ctrlPts[(size_t)i][0]; const f64* p2 = &ctrlPts[(size_t)(i+1)][0]; const f64* p3 = (i + 2 < n) ? &ctrlPts[(size_t)(i+2)][0] : &ctrlPts[(size_t)(n-1)][0]; for (int s = 1; s <= stepsPerSeg; ++s) { const f64 t = (f64)s / (f64)stepsPerSeg; const f64 t2 = t * t, t3 = t2 * t; // Catmull-Rom formülü const f64 x = 0.5 * (2.0*p1[0] + (-p0[0]+p2[0])*t + (2.0*p0[0]-5.0*p1[0]+4.0*p2[0]-p3[0])*t2 + (-p0[0]+3.0*p1[0]-3.0*p2[0]+p3[0])*t3); const f64 y = 0.5 * (2.0*p1[1] + (-p0[1]+p2[1])*t + (2.0*p0[1]-5.0*p1[1]+4.0*p2[1]-p3[1])*t2 + (-p0[1]+3.0*p1[1]-3.0*p2[1]+p3[1])*t3); ImVec2 cur; if (ekUV(x, y, cur)) { if (oncekiVar) ciz->AddLine(onceki, cur, IM_COL32(80, 220, 255, 180), 2.0f); onceki = cur; oncekiVar = true; } else { oncekiVar = false; } } } } // İmleç ucu: SADECE "buraya tıklarsan sıradaki nokta burası olur" ipucu — // kesikli çizgi + turuncu halka. Eğri/çokgen hesabına DAHİL DEĞİL, bu yüzden // sağ-tıkla Tamamla anında görsel bir "kayıp/kapanma" hissi vermez. { const f64 mu = sonSnap.u, mv = sonSnap.v; ImVec2 s; if (ekUV(mu, mv, s)) { ciz->AddCircle(s, 5.0f, cpImlec, 12, 1.6f); const auto& son = sketchNoktalar.back(); ImVec2 p0; if (ekUV(son[0], son[1], p0)) ciz->AddLine(p0, s, cpImlec, 1.2f); // ince çizgi = "sıradaki nokta" ipucu } } } // (1b) Sonsuz çizgi H/V modları: tık öncesi imleçten geçen uzun çizgi önizleme. if (aktifMi && sketchArac == "sonsuzCizgi" && sketchNoktalar.empty() && vpHover && sketch.sonsuzMod != 2 && sonSnap.tip != eskiz::SnapTip::Yok) { if (sketch.sonsuzMod == 0) onCizgi(sonSnap.u - 1e4, sonSnap.v, sonSnap.u + 1e4, sonSnap.v); else onCizgi(sonSnap.u, sonSnap.v - 1e4, sonSnap.u, sonSnap.v + 1e4); } // (2) Düzenleme araçları önizleme: pah/fillet 2. çizgi seçiliyken sonuç // yayını/pah çizgisini canlı göster (mesafe = imleçten köşeye izdüşüm veya // giriş değeri). Kırp/uzat imleç-altı vurgulanan geo zaten cyan boyalı. if (aktifMi && (sketchArac == "pah" || sketchArac == "fillet") && sketchSecili.size() >= 2) { const eskiz::Geometri* gA = ev.geoBul(sketchSecili[0]); const eskiz::Geometri* gB = ev.geoBul(sketchSecili[1]); if (gA && gB && gA->tip == eskiz::GeoTip::Cizgi && gB->tip == eskiz::GeoTip::Cizgi) { // İki doğrunun kesişim köşesi + köşeden uzak birim yönler. const f64 A1x=ev.noktaX(gA->kimlik,0), A1y=ev.noktaY(gA->kimlik,0); const f64 A2x=ev.noktaX(gA->kimlik,1), A2y=ev.noktaY(gA->kimlik,1); const f64 B1x=ev.noktaX(gB->kimlik,0), B1y=ev.noktaY(gB->kimlik,0); const f64 B2x=ev.noktaX(gB->kimlik,1), B2y=ev.noktaY(gB->kimlik,1); const f64 rx=A2x-A1x, ry=A2y-A1y, sx=B2x-B1x, sy=B2y-B1y; const f64 payda=rx*sy-ry*sx; if (std::abs(payda) > 1e-9) { const f64 tt=((B1x-A1x)*sy-(B1y-A1y)*sx)/payda; const f64 kx=A1x+rx*tt, ky=A1y+ry*tt; // köşe auto yon=[&](f64 p1x,f64 p1y,f64 p2x,f64 p2y,f64& ux,f64& uy){ const f64 d1=std::hypot(p1x-kx,p1y-ky), d2=std::hypot(p2x-kx,p2y-ky); const f64 ux0=(d1>d2?p1x:p2x)-kx, uy0=(d1>d2?p1y:p2y)-ky; const f64 L=std::hypot(ux0,uy0); ux=L>1e-9?ux0/L:0; uy=L>1e-9?uy0/L:0; }; f64 uAx,uAy,uBx,uBy; yon(A1x,A1y,A2x,A2y,uAx,uAy); yon(B1x,B1y,B2x,B2y,uBx,uBy); // Giriş değeri: kutu doluysa onu, yoksa imleç izdüşümü. pah → mesafe, // fillet → yarıçap (koseYuvarlat/pah imzalarıyla aynı anlam). f64 girVal = sketchDuzGiris[0][0] ? std::strtod(sketchDuzGiris[0], nullptr) : 0.0; if (girVal <= 1e-9 && vpHover) { const f64 pu=sonSnap.u-kx, pv=sonSnap.v-ky; girVal = std::max(std::abs(pu*uAx+pv*uAy), std::abs(pu*uBx+pv*uBy)); } if (girVal > 1e-9) { onCizgi(A1x,A1y,A2x,A2y); onCizgi(B1x,B1y,B2x,B2y); // referans çizgiler if (sketchArac == "pah") { // Ölçü moduna göre iki kenar setback (kutu boşsa imleç → simetrik). f64 mA = girVal, mB = girVal; if (sketchDuzGiris[0][0]) { const f64 v0 = std::strtod(sketchDuzGiris[0], nullptr); const f64 v1 = sketchDuzGiris[1][0] ? std::strtod(sketchDuzGiris[1], nullptr) : (sketch.pahOlcuMod == 1 ? v0 : 45.0); pahMesafeHesapla(sketch.pahOlcuMod, v0, v1, mA, mB); } const f64 PAx=kx+uAx*mA, PAy=ky+uAy*mA; const f64 PBx=kx+uBx*mB, PBy=ky+uBy*mB; onCizgi(PAx,PAy,PBx,PBy); onNokta(PAx,PAy); onNokta(PBx,PBy); } else { // fillet: girVal = yarıçap → teget yay const f64 kt=std::max(-1.0,std::min(1.0,uAx*uBx+uAy*uBy)); const f64 th=std::acos(kt); if (th > 1e-6 && std::abs(th-std::numbers::pi) > 1e-6) { const f64 r = girVal; const f64 tegetMes = r / std::tan(th/2.0); const f64 PAx=kx+uAx*tegetMes, PAy=ky+uAy*tegetMes; const f64 PBx=kx+uBx*tegetMes, PBy=ky+uBy*tegetMes; f64 ox=uAx+uBx, oy=uAy+uBy; const f64 oL=std::hypot(ox,oy); if (oL > 1e-9) { ox/=oL; oy/=oL; const f64 mM = r/std::sin(th/2.0); const f64 Mx=kx+ox*mM, My=ky+oy*mM; const f64 a0=std::atan2(PAy-My,PAx-Mx), a1=std::atan2(PBy-My,PBx-Mx); auto nrm=[](f64 a){ constexpr f64 t=2.0*std::numbers::pi; while(a<-std::numbers::pi)a+=t; while(a>=std::numbers::pi)a-=t; return a; }; onYay(Mx, My, r, a0, a0 + nrm(a1-a0)); } } } } } } } // ---- Sketch Analizi: açık uçları KIRMIZI halka ile işaretle (analiz penceresi açıkken) ---- // CATIA Sketch Analysis: dangling uçlar (profil kapanmıyor) görsel vurgulanır. if (aktifMi && sketchAnalizAcik) { const auto an = ev.analiz(); for (const auto& uc : an.acikUclar) { ImVec2 s; if (ekUV(uc.first, uc.second, s)) { ciz->AddCircle(s, 9.0f, IM_COL32(255, 65, 55, 255), 20, 2.6f); ciz->AddCircleFilled(s, 3.0f, IM_COL32(255, 110, 95, 220)); } } } // ---- Snap marker + inference (SmartPick görsel geri bildirim) ---- if (aktifMi && vpHover && sonSnap.tip != eskiz::SnapTip::Yok && sonSnap.tip != eskiz::SnapTip::Serbest) { ImVec2 m; if (ekUV(sonSnap.u, sonSnap.v, m)) { const f32 r = 5.0f; switch (sonSnap.tip) { case eskiz::SnapTip::UcNokta: // □ ciz->AddRect({m.x - r, m.y - r}, {m.x + r, m.y + r}, snapRenk, 0, 0, 2.0f); break; case eskiz::SnapTip::Merkez: // ○ ciz->AddCircle(m, r, snapRenk, 16, 2.0f); break; case eskiz::SnapTip::OrtaNokta: // △ ciz->AddTriangle({m.x, m.y - r}, {m.x - r, m.y + r}, {m.x + r, m.y + r}, snapRenk, 2.0f); break; case eskiz::SnapTip::Kadran: // ◇ ciz->AddQuad({m.x, m.y - r}, {m.x + r, m.y}, {m.x, m.y + r}, {m.x - r, m.y}, snapRenk, 2.0f); break; case eskiz::SnapTip::Kesisim: // ⋈ (çapraz) ciz->AddLine({m.x - r, m.y - r}, {m.x + r, m.y + r}, snapRenk, 2.0f); ciz->AddLine({m.x - r, m.y + r}, {m.x + r, m.y - r}, snapRenk, 2.0f); break; case eskiz::SnapTip::Izgara: // + ciz->AddLine({m.x - r, m.y}, {m.x + r, m.y}, snapRenk, 1.6f); ciz->AddLine({m.x, m.y - r}, {m.x, m.y + r}, snapRenk, 1.6f); break; default: // NesneUzeri / Yatay / Dikey / Aci → ⋈ ciz->AddLine({m.x - r, m.y - r}, {m.x + r, m.y + r}, snapRenk, 1.6f); ciz->AddLine({m.x - r, m.y + r}, {m.x + r, m.y - r}, snapRenk, 1.6f); break; } // Inference hiza çizgisi (referans noktadan snap noktasına kesikli). if (sonSnap.yatayHiza || sonSnap.dikeyHiza) { ImVec2 h; if (ekUV(sonSnap.hizaU, sonSnap.hizaV, h)) { const ImU32 hizaRenk = sonSnap.yatayHiza ? IM_COL32(120, 220, 255, 190) : IM_COL32(235, 235, 240, 190); kesikliCiz(h, m, hizaRenk, 1.4f); } } } } } } } // ============================================================================ // Sahnede sağ-tık bağlam menüsü — duruma göre özel öğeler (CATIA sağ-tık menüsü). // • Kısıt sembolü üstünde → Sürücü/Referans + Sil // • Geometri seçili/üstünde → Kısıt Uygula ▸ + Yapı/Standart + Dönüştür ▸ + Sil // • Boş alan → Araç ▸ + Düzenle ▸ + Grid/Snap/Analiz toggle + Tümünü Seç + Çıkış // main.cpp sağ-tık işleyicisi hedefi (baglamGeo/baglamKisit) çözüp baglamAc=true set eder. // ============================================================================ void sketchBaglamMenuCiz(UygulamaBaglam& b) { auto& sketch = b.sketch; if (sketch.baglamAc) { ImGui::OpenPopup("##sketchBaglam"); sketch.baglamAc = false; } SketchNesnesi* skP = b.sketchNesne(); if (!skP) return; eskiz::EskizVeri& ev = skP->eskiz(); if (!ImGui::BeginPopup("##sketchBaglam")) return; const u32 hedefKisit = sketch.baglamKisit; const u32 hedefGeo = sketch.baglamGeo; std::vector& sec = sketch.sketchSecili; Cekirdek& cekirdek = b.cekirdek; if (hedefKisit) { // ---- Kısıt bağlam menüsü ---- const eskiz::Kisit* k = nullptr; for (const eskiz::Kisit& kk : ev.kisitlar()) if (kk.kimlik == hedefKisit) { k = &kk; break; } ImGui::TextDisabled("Kısıt"); ImGui::Separator(); if (k && kisitBoyutsalMi(k->tip)) { if (ImGui::MenuItem("Değer...")) { // çift-tık ile aynı kutu (keşfedilebilirlik) sketch.kisitDuzenle = hedefKisit; sketch.kisitDuzenleIlk = true; } if (ImGui::MenuItem(k->suru ? "Referansa Çevir" : "Sürücüye Çevir")) { ev.kisitSuruAyarla(hedefKisit, !k->suru); ev.coz(); } if (ImGui::MenuItem("Animasyon...")) sketchAnimasyonAc(b, hedefKisit); } if (ImGui::MenuItem("Sil")) { ev.kisitSil(hedefKisit); ev.coz(); auto& sk = sketch.sketchSeciliKisit; sk.erase(std::remove(sk.begin(), sk.end(), hedefKisit), sk.end()); b.bilgiVer("Kısıt silindi"); } } else if (hedefGeo || !sec.empty()) { // ---- Geometri bağlam menüsü ---- ImGui::TextDisabled("%zu öğe", sec.empty() ? 1u : sec.size()); ImGui::Separator(); if (ImGui::BeginMenu("Kısıt Uygula")) { struct { const char* ad; eskiz::KisitTip tip; } ks[] = { {"Yatay", eskiz::KisitTip::Yatay}, {"Dikey", eskiz::KisitTip::Dikey}, {"Dik", eskiz::KisitTip::Dik}, {"Paralel", eskiz::KisitTip::Paralel}, {"Eşit", eskiz::KisitTip::Esit}, {"Çakışık", eskiz::KisitTip::Cakisik}, {"Mesafe", eskiz::KisitTip::Mesafe}, {"Yarıçap", eskiz::KisitTip::Yaricap}}; for (auto& x : ks) if (ImGui::MenuItem(x.ad)) b.sketchKisitUygula(x.tip); ImGui::Separator(); if (ImGui::MenuItem("Temas (oto)")) b.sketchTemasKisit(); ImGui::EndMenu(); } if (ImGui::MenuItem("Oto Ölçü")) b.sketchOtoOlcu(); if (ImGui::MenuItem("Oto Kısıt")) b.sketchOtoKisit(); // Yapı/Standart çevir (hedef veya ilk seçili geometrinin durumuna göre). const u32 ilk = hedefGeo ? hedefGeo : (sec.empty() ? 0u : sec[0]); const eskiz::Geometri* g = ilk ? ev.geoBul(ilk) : nullptr; if (g && ImGui::MenuItem(g->yapi ? "Standart Elemana Çevir" : "Yapı Elemanına Çevir")) { const bool yeni = !g->yapi; if (sec.empty()) ev.yapiAyarla(ilk, yeni); else for (u32 id : sec) ev.yapiAyarla(id, yeni); ev.coz(); } if (ImGui::BeginMenu("Dönüştür")) { if (ImGui::MenuItem("Ötele")) b.sketchDuzenSec("otele"); if (ImGui::MenuItem("Döndür")) b.sketchDuzenSec("dondur"); if (ImGui::MenuItem("Ölçekle")) b.sketchDuzenSec("olcekle"); if (ImGui::MenuItem("Ayna")) b.sketchDuzenSec("ayna"); if (ImGui::MenuItem("Ofset")) b.sketchDuzenSec("ofset"); ImGui::EndMenu(); } ImGui::Separator(); if (ImGui::MenuItem("Sil")) { if (sec.empty()) { if (ilk) ev.geoSil(ilk); } else for (u32 id : sec) ev.geoSil(id); sec.clear(); ev.coz(); b.bilgiVer("Geometri silindi"); } } else { // ---- Boş alan bağlam menüsü ---- ImGui::TextDisabled("Sketch"); ImGui::Separator(); if (ImGui::BeginMenu("Araç")) { struct { const char* ad; const char* ar; } ar[] = { {"Çizgi", "cizgi"}, {"Sonsuz Çizgi", "sonsuzCizgi"}, {"Dikdörtgen", "dikdortgen"}, {"Daire", "daire"}, {"Daire (Koord)", "daireKoord"}, {"Yay (3N)", "yay3"}, {"Merkez Yay", "merkezYay"}, {"Elips", "elips"}, {"Eliptik Yay", "elipsYay"}, {"Slot", "slot"}, {"Eğrisel Slot", "egriselSlot"}, {"Anahtar Deliği", "anahtarDeligi"}, {"Çokgen", "cokgen"}, {"Nokta", "nokta"}, {"Nokta (Koord)", "noktaKoord"}, {"Spline", "spline"}, {"NURBS Egri", "nurbsEgri"}}; for (auto& x : ar) if (ImGui::MenuItem(x.ad)) b.sketchAracSec(x.ar); ImGui::EndMenu(); } if (ImGui::BeginMenu("Düzenle")) { struct { const char* ad; const char* ar; } dz[] = { {"Kırp", "trim"}, {"Köşe", "fillet"}, {"Pah", "pah"}, {"Ayna", "ayna"}, {"Ötele", "otele"}, {"Ofset", "ofset"}}; for (auto& x : dz) if (ImGui::MenuItem(x.ad)) b.sketchDuzenSec(x.ar); ImGui::EndMenu(); } ImGui::Separator(); ImGui::MenuItem("Izgara", nullptr, &sketch.sketchGrid); ImGui::MenuItem("Izgaraya Snap", nullptr, &sketch.sketchGridSnap); ImGui::MenuItem("Yapı Modu", nullptr, &sketch.sketchYapiMod); ImGui::MenuItem("Sketch Analizi", nullptr, &sketch.sketchAnalizAcik); ImGui::Separator(); if (ImGui::MenuItem("Tümünü Seç")) { sec.clear(); for (const eskiz::Geometri& gg : ev.geometriler()) sec.push_back(gg.kimlik); } if (ImGui::MenuItem("Sketch'ten Çık")) { for (const MenuOge& o : cekirdek.menuler().hepsi()) if (o.grup == "Sketch" && o.ad == "Sketch'ten Çık") { cekirdek.komutCalistir(o, "sketch"); break; } } } ImGui::EndPopup(); } // ============================================================================ // CATIA "Sketch Analysis" penceresi — aktif sketch'in geometrilerini tablo // halinde listeler (Geometri · Durum · Yorum) + üstte General Status + altta // düzeltici aksiyonlar (Yapı Moduna Al / Açık Profili Kapat / Sil / gizle // toggle'ları). Açık uç tespiti TEK OTORİTE = EskizVeri::analiz() (overlay'deki // kırmızı-halka işareti aynı veriyi kullanır). b.sketch.sketchAnalizAcik iken çizer; // mini panel/bağlam menüsündeki "Analiz" toggle'ı görünürlüğü açar/kapar. // ============================================================================ void sketchAnalizPenceresiCiz(UygulamaBaglam& b) { auto& sketch = b.sketch; if (!sketch.sketchAnalizAcik) return; ImGui::SetNextWindowBgAlpha(0.96f); ui::Pencere pen{"Sketch Analizi###sketchAnaliz", &sketch.sketchAnalizAcik, {.boyut = {360, 430}}}; if (!pen) return; // yıkıcı End() garantili (RAII) SketchNesnesi* skP = b.sketchNesne(); if (!skP) { ImGui::TextDisabled("Aktif sketch yok."); ImGui::TextWrapped("Düzlem/düz yüz seçip 'Sketch Oluştur' çalıştırın."); return; } eskiz::EskizVeri& ev = skP->eskiz(); const auto an = ev.analiz(); // ---- General Status (CATIA "General Status" satırı) ---- const bool bos = ev.geometriler().empty(); const int acikSay = (int)an.acikUclar.size(); if (bos) ImGui::TextColored({0.55f, 0.60f, 0.68f, 1.0f}, "General Status: profil boş"); else if (acikSay == 0) ImGui::TextColored({0.35f, 0.86f, 0.55f, 1.0f}, "✔ Kapalı — katı için uygun"); else ImGui::TextColored({0.95f, 0.65f, 0.30f, 1.0f}, "⚠ %d açık uç — profil kapanmıyor", acikSay); ImGui::SameLine(ImGui::GetContentRegionAvail().x - 66); ImGui::TextDisabled("DOF %d", an.dof); if (an.izoleNokta > 0) ImGui::TextColored({0.60f, 0.72f, 0.95f, 1.0f}, "%d izole nokta", an.izoleNokta); ImGui::Separator(); // Bir uç açık uç mu? (analiz() dangling konumlarıyla yakınlık testi.) const auto acikUcMu = [&](f64 x, f64 y) { for (const auto& u : an.acikUclar) if (std::hypot(u.first - x, u.second - y) < 1e-3) return true; return false; }; const auto tipAd = [](eskiz::GeoTip t) -> const char* { switch (t) { case eskiz::GeoTip::Nokta: return "Nokta"; case eskiz::GeoTip::Cizgi: return "Çizgi"; case eskiz::GeoTip::Daire: return "Çember"; case eskiz::GeoTip::Yay: return "Yay"; case eskiz::GeoTip::Elips: return "Elips"; case eskiz::GeoTip::Spline: return "Spline"; } return "?"; }; // ---- Geometri tablosu (Geometri · Durum · Yorum) ---- static u32 seciliGeo = 0; bool secGecerli = false; if (ImGui::BeginTable("sanalizTablo", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_SizingStretchProp, {-1, -128})) { ImGui::TableSetupColumn("Geometri", ImGuiTableColumnFlags_WidthStretch, 0.30f); ImGui::TableSetupColumn("Durum", ImGuiTableColumnFlags_WidthFixed, 62.0f); ImGui::TableSetupColumn("Yorum", ImGuiTableColumnFlags_WidthStretch, 0.55f); ImGui::TableHeadersRow(); int sira = 0; for (const eskiz::Geometri& g : ev.geometriler()) { ++sira; const char* durum; ImVec4 dr; const char* yorum; if (g.yapi) { durum = "Yapı"; dr = {0.55f, 0.62f, 0.72f, 1.0f}; yorum = "Yapı geometrisi (profil-dışı)"; } else if (g.tip == eskiz::GeoTip::Nokta) { durum = "İzole"; dr = {0.60f, 0.72f, 0.95f, 1.0f}; yorum = "Serbest nokta"; } else if (g.tip == eskiz::GeoTip::Daire || g.tip == eskiz::GeoTip::Elips) { durum = "Kapalı"; dr = {0.35f, 0.86f, 0.55f, 1.0f}; yorum = "Kendi başına kapalı kontur"; } else { // Çizgi / Yay: uç bağlantısına göre const bool u0 = acikUcMu(ev.noktaX(g.kimlik, 0), ev.noktaY(g.kimlik, 0)); const bool u1 = acikUcMu(ev.noktaX(g.kimlik, 1), ev.noktaY(g.kimlik, 1)); if (u0 && u1) { durum = "İzole"; dr = {0.95f, 0.55f, 0.35f, 1.0f}; yorum = "İki ucu da bağlı değil"; } else if (u0 || u1) { durum = "Açık"; dr = {0.95f, 0.72f, 0.30f, 1.0f}; yorum = "Bir ucu açık — profil kapanmıyor"; } else { durum = "Kapalı"; dr = {0.35f, 0.86f, 0.55f, 1.0f}; yorum = "Uçları komşulara bağlı"; } } if (g.kimlik == seciliGeo) secGecerli = true; ImGui::PushID((int)g.kimlik); ImGui::TableNextRow(); ImGui::TableNextColumn(); char et[48]; std::snprintf(et, sizeof(et), "%s.%d", tipAd(g.tip), sira); if (ImGui::Selectable(et, g.kimlik == seciliGeo, ImGuiSelectableFlags_SpanAllColumns)) seciliGeo = g.kimlik; ImGui::TableNextColumn(); ImGui::TextColored(dr, "%s", durum); ImGui::TableNextColumn(); ImGui::TextDisabled("%s", yorum); ImGui::PopID(); } ImGui::EndTable(); } if (!secGecerli) seciliGeo = 0; // silinen/geçersiz seçim temizle // ---- Düzeltici Aksiyonlar (seçili satır geometrisine) ---- ImGui::Separator(); ImGui::TextColored({0.55f, 0.60f, 0.68f, 1.0f}, "Düzeltici Aksiyonlar"); const eskiz::Geometri* sg = seciliGeo ? ev.geoBul(seciliGeo) : nullptr; if (!sg) ImGui::BeginDisabled(); const f32 yariG = (ImGui::GetContentRegionAvail().x - 6.0f) * 0.5f; if (ImGui::Button("Yapı Moduna Al", {yariG, 0})) { ev.yapiAyarla(seciliGeo, true); ev.coz(); b.bilgiVer("Yapı elemanına çevrildi"); } ImGui::SameLine(); if (ImGui::Button("Açık Profili Kapat", {yariG, 0})) { if (ev.kapat(seciliGeo)) { ev.coz(); b.bilgiVer("Kontur kapatıldı"); } else b.bilgiVer("Bu geometri kapatılamaz"); } if (ImGui::Button("Geometriyi Sil", {-1, 0})) { ev.geoSil(seciliGeo); ev.coz(); seciliGeo = 0; b.bilgiVer("Geometri silindi"); } if (!sg) ImGui::EndDisabled(); ImGui::Spacing(); ImGui::Checkbox("Kısıtları Gizle", &sketch.sketchKisitGizle); ImGui::SameLine(); ImGui::Checkbox("Yapı Geometrisini Gizle", &sketch.sketchYapiGizle); } // ============================================================================ // CATIA "Animate Constraint" (PDF p33-34) — seçili BOYUTSAL kısıtın değerini // İlk→Son arasında N adımda değiştirir; her adımda eskiz yeniden çözülür → // 2B mekanizma animasyonu. Stop/pencere kapanışında kısıt İLK değere geri // döner (mekanizma bozulmaz). Açı kısıtı pencerede DERECE gösterir/okur, // motora RADYAN yazar (Kisit.deger radyan). // ============================================================================ void sketchAnimasyonAc(UygulamaBaglam& b, u32 kisitKimlik) { auto& sketch = b.sketch; SketchNesnesi* skP = b.sketchNesne(); if (!skP) { b.bilgiVer("Aktif sketch yok"); return; } u32 hedef = kisitKimlik; if (!hedef && !sketch.sketchSeciliKisit.empty()) hedef = sketch.sketchSeciliKisit[0]; const eskiz::Kisit* k = nullptr; for (const eskiz::Kisit& kk : skP->eskiz().kisitlar()) if (kk.kimlik == hedef) { k = &kk; break; } if (!k) { b.bilgiVer("Animasyon: önce sahnede bir boyutsal kısıt seçin"); return; } if (!kisitBoyutsalMi(k->tip)) { b.bilgiVer("Animasyon yalnız boyutsal kısıtlara (Mesafe/Açı/Yarıçap...) uygulanır"); return; } sketch.animAcik = true; sketch.animKisit = hedef; sketch.animIlk = sketch.animSon = k->deger; // başlangıç: güncel değer sketch.animIndeks = 0; sketch.animYon = 1; sketch.animOynat = 0; sketch.animSayac = 0.0f; } // ============================================================================ // Çift-tık boyut değeri düzenleme (CATIA): küçük yüzer kutu, Enter=uygula+çöz, // Esc/odak kaybı=vazgeç. Açı kısıtı DERECE gösterir/okur, motora RADYAN yazar. // Referans (suru=false) ölçü düzenlenmez — değeri geometri sürer. // ============================================================================ void sketchKisitDegerPenceresiCiz(UygulamaBaglam& b) { auto& sketch = b.sketch; if (!sketch.kisitDuzenle) return; SketchNesnesi* skP = b.sketchNesne(); const eskiz::Kisit* k = nullptr; if (skP) for (const eskiz::Kisit& kk : skP->eskiz().kisitlar()) if (kk.kimlik == sketch.kisitDuzenle) { k = &kk; break; } if (!k || !kisitBoyutsalMi(k->tip)) { sketch.kisitDuzenle = 0; return; } if (!k->suru) { b.bilgiVer("Referans ölçü düzenlenemez — önce sağ-tık 'Sürücüye Çevir'"); sketch.kisitDuzenle = 0; return; } const bool aciMi = k->tip == eskiz::KisitTip::Aci; if (sketch.kisitDuzenleIlk) { std::snprintf(sketch.kisitDuzenleGiris, sizeof sketch.kisitDuzenleGiris, "%.6g", aciMi ? k->deger * 180.0 / std::numbers::pi : k->deger); ImGui::SetNextWindowPos({ImGui::GetIO().MousePos.x + 12, ImGui::GetIO().MousePos.y - 8}); } ImGui::SetNextWindowBgAlpha(0.94f); bool acik = true; if (ImGui::Begin("Boyut", &acik, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings)) { ImGui::TextDisabled("%s%s", kisitTipAd(k->tip), aciMi ? " (°)" : ""); if (sketch.kisitDuzenleIlk) ImGui::SetKeyboardFocusHere(); ImGui::SetNextItemWidth(110); const bool girildi = ImGui::InputText("##boyutDeger", sketch.kisitDuzenleGiris, sizeof sketch.kisitDuzenleGiris, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CharsDecimal); ImGui::SameLine(); const bool onay = ImGui::SmallButton("✔"); if (girildi || onay) { const f64 v = std::atof(sketch.kisitDuzenleGiris); eskiz::EskizVeri& ev = skP->eskiz(); ev.kisitDegerAyarla(sketch.kisitDuzenle, aciMi ? v * std::numbers::pi / 180.0 : v); ev.coz(); sketch.kisitDuzenle = 0; } if (ImGui::IsKeyPressed(ImGuiKey_Escape)) sketch.kisitDuzenle = 0; } ImGui::End(); if (!acik) sketch.kisitDuzenle = 0; sketch.kisitDuzenleIlk = false; } void sketchAnimasyonPenceresiCiz(UygulamaBaglam& b) { auto& sketch = b.sketch; if (!sketch.animAcik) return; // Guard: sketch kapandı / kısıt silindi → pencereyi kapat (dönülecek hedef yok). SketchNesnesi* skP = b.sketchNesne(); const eskiz::Kisit* k = nullptr; if (skP) for (const eskiz::Kisit& kk : skP->eskiz().kisitlar()) if (kk.kimlik == sketch.animKisit) { k = &kk; break; } if (!k) { sketch.animAcik = false; sketch.animOynat = 0; return; } eskiz::EskizVeri& ev = skP->eskiz(); const bool aciMi = k->tip == eskiz::KisitTip::Aci; const f64 birim = aciMi ? 180.0 / std::numbers::pi : 1.0; // gösterim çarpanı // i. adım değerini kısıta yaz + çöz; çözüm çelişkiliyse oynatmayı durdur. const auto uygula = [&](int i) { const f64 t = sketch.animAdim > 0 ? (f64)i / (f64)sketch.animAdim : 1.0; ev.kisitDegerAyarla(sketch.animKisit, sketch.animIlk + (sketch.animSon - sketch.animIlk) * t); if (ev.coz().durum == eskiz::Solver::Durum::Celiskili) { sketch.animOynat = 0; b.bilgiVer("Animasyon durduruldu: çözüm çelişkili"); } }; // Kısıtı İLK değere geri döndür (Stop / pencere kapanışı). const auto geriDon = [&]() { ev.kisitDegerAyarla(sketch.animKisit, sketch.animIlk); ev.coz(); sketch.animIndeks = 0; sketch.animYon = 1; sketch.animOynat = 0; sketch.animSayac = 0.0f; }; ImGui::SetNextWindowBgAlpha(0.96f); ui::Pencere pen{"Kısıt Animasyonu###sketchAnim", &sketch.animAcik, {.boyut = {286, 0}}}; if (!pen) { if (!sketch.animAcik) geriDon(); // ✕ ile kapatıldı → ilk değere dön return; // yıkıcı End() garantili (RAII) } const char* tipAd; switch (k->tip) { case eskiz::KisitTip::Mesafe: tipAd = "Mesafe"; break; case eskiz::KisitTip::YatayMesafe: tipAd = "Yatay Mesafe"; break; case eskiz::KisitTip::DikeyMesafe: tipAd = "Dikey Mesafe"; break; case eskiz::KisitTip::Yaricap: tipAd = "Yarıçap"; break; case eskiz::KisitTip::Cap: tipAd = "Çap"; break; case eskiz::KisitTip::Aci: tipAd = "Açı"; break; default: tipAd = "Kısıt"; break; } ImGui::TextDisabled("%s kısıtı · güncel %.3f%s", tipAd, k->deger * birim, aciMi ? "°" : ""); ImGui::Separator(); f64 ilkG = sketch.animIlk * birim, sonG = sketch.animSon * birim; ImGui::SetNextItemWidth(120); if (ImGui::InputDouble(aciMi ? "İlk değer (°)" : "İlk değer", &ilkG, 0, 0, "%.3f")) sketch.animIlk = ilkG / birim; ImGui::SetNextItemWidth(120); if (ImGui::InputDouble(aciMi ? "Son değer (°)" : "Son değer", &sonG, 0, 0, "%.3f")) sketch.animSon = sonG / birim; ImGui::SetNextItemWidth(120); if (ImGui::InputInt("Adım sayısı", &sketch.animAdim)) { sketch.animAdim = std::clamp(sketch.animAdim, 1, 1000); sketch.animIndeks = std::min(sketch.animIndeks, sketch.animAdim); } ImGui::SetNextItemWidth(120); ImGui::SliderFloat("Hız (adım/sn)", &sketch.animHiz, 0.5f, 60.0f, "%.1f", ImGuiSliderFlags_Logarithmic); ImGui::SetNextItemWidth(120); ImGui::Combo("Mod", &sketch.animDongu, "Bir kez\0İleri-geri\0Döngü\0"); ImGui::Checkbox("Kısıtları gizle", &sketch.sketchKisitGizle); ImGui::Separator(); // Adım kaydırıcı (scrub): sürüklenince değer anında uygulanır. int ix = sketch.animIndeks; ImGui::SetNextItemWidth(-1); if (ImGui::SliderInt("##animIx", &ix, 0, sketch.animAdim, "adım %d")) { sketch.animIndeks = ix; uygula(ix); } // Taşıma kontrolleri: başa · tek-adım geri · oynat/duraklat · tek-adım ileri · stop. if (ImGui::Button("⏮")) { sketch.animIndeks = 0; sketch.animYon = 1; uygula(0); } ImGui::SameLine(); if (ImGui::Button("◀") && sketch.animIndeks > 0) uygula(--sketch.animIndeks); ImGui::SameLine(); if (ImGui::Button(sketch.animOynat ? "⏸ Duraklat" : "▶ Oynat", {96, 0})) { if (!sketch.animOynat && sketch.animDongu == 0 && sketch.animIndeks >= sketch.animAdim) { sketch.animIndeks = 0; // "bir kez" sonda kaldıysa baştan başlat uygula(0); } sketch.animOynat = !sketch.animOynat; sketch.animSayac = 0.0f; } ImGui::SameLine(); if (ImGui::Button("▶##adim") && sketch.animIndeks < sketch.animAdim) uygula(++sketch.animIndeks); ImGui::SameLine(); if (ImGui::Button("⏹")) geriDon(); // Oynatma ilerletme: DeltaTime · hız birikimi, kare başına en fazla 1 adım. if (sketch.animOynat) { sketch.animSayac += ImGui::GetIO().DeltaTime * sketch.animHiz; if (sketch.animSayac >= 1.0f) { sketch.animSayac = 0.0f; int i = sketch.animIndeks + sketch.animYon; if (i > sketch.animAdim || i < 0) { if (sketch.animDongu == 1) { // ileri-geri: uçta yön çevir sketch.animYon = -sketch.animYon; i = sketch.animIndeks + sketch.animYon; } else if (sketch.animDongu == 2) { // döngü: başa sar i = 0; } else { // bir kez: sonda dur sketch.animOynat = 0; i = sketch.animIndeks; } } if (i != sketch.animIndeks) { sketch.animIndeks = i; uygula(i); } } } if (!sketch.animAcik) geriDon(); // ✕ ile kapatıldı → ilk değere dön (yıkıcı End()) } } // namespace fiko