#include "pafta_ciz.hpp" #include #include #include #include #include #include #include #include #include #include "imgui.h" #include "pafta_ui.hpp" #include "ui_ortak.hpp" #include "uygulama_baglam.hpp" #include "fiko/cekirdek/gorev.hpp" #include "fiko/core/kare_istek.hpp" #include "fiko/pafta/pafta_dxf.hpp" #include "fiko/pmi/pmi.hpp" #include "fiko/platform/pencere.hpp" #include "fiko/sahne/pafta_nesne.hpp" #include "fiko/sahne/sahne_islem.hpp" #include "fiko/sahne/sahne.hpp" namespace fiko { f64& paftaKenarPayiPx() { static f64 v = 16.0; return v; } f64& paftaVurToleransMm() { static f64 v = 2.0; return v; } f32& paftaCizgiKalinlik() { static f32 v = 1.4f; return v; } f32& paftaZoomAdim() { static f32 v = 1.12f; return v; } f32& paftaYaziMm() { static f32 v = 3.5f; return v; } // ölçü metni SAYFA mm boyu bool& paftaYaziSayfaOlcekli() { static bool v = true; return v; } // kapalı = eski px-sabit f64& paftaTutunmaPx() { static f64 v = 10.0; return v; } f64& paftaOkBoyuMm() { static f64 v = 3.0; return v; } f64& paftaTaramaAci() { static f64 v = 45.0; return v; } f64& paftaTaramaAralikMm() { static f64 v = 3.0; return v; } bool& paftaPmiGoster() { static bool v = true; return v; } // 3B PMI paftaya yansır (kapalı = eski) namespace { constexpr ImU32 KAGIT = IM_COL32(248, 248, 245, 255); constexpr ImU32 GOLGE = IM_COL32(0, 0, 0, 70); constexpr ImU32 CERCEVE = IM_COL32(40, 44, 52, 255); constexpr ImU32 CIZGI = IM_COL32(24, 26, 30, 255); constexpr ImU32 GIZLI = IM_COL32(24, 26, 30, 130); constexpr ImU32 SECILI = IM_COL32(70, 210, 235, 255); constexpr ImU32 SOLUK = IM_COL32(120, 128, 140, 160); ImVec2 iv(const pafta::SayfaDonusum& d, Vec2d sayfa) { const Vec2d e = d.ekrana(sayfa); return {(f32)e.x, (f32)e.y}; } // Kesikli polyline (gizli çizgi) — ImGui'de yerel kesik yok, parçalara böleriz. void kesikliCiz(ImDrawList* ciz, const std::vector& nok, ImU32 renk, f32 kalinlik, f32 bolme) { f32 kalan = bolme; bool ciziyor = true; for (size_t i = 0; i + 1 < nok.size(); ++i) { ImVec2 a = nok[i]; const ImVec2 b = nok[i + 1]; f32 uzunluk = std::sqrt((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y)); if (uzunluk <= 0.0f) continue; f32 ux = (b.x - a.x) / uzunluk, uy = (b.y - a.y) / uzunluk; while (uzunluk > 0.0f) { const f32 adim = std::min(kalan, uzunluk); const ImVec2 s{a.x + ux * adim, a.y + uy * adim}; if (ciziyor) ciz->AddLine(a, s, renk, kalinlik); a = s; uzunluk -= adim; kalan -= adim; if (kalan <= 0.0f) { kalan = bolme; ciziyor = !ciziyor; } } } } // Nokta–parça mesafesi (ölçü seçimi için; görünüm-yerel mm). f64 parcayaUzaklik(Vec2d p, Vec2d a, Vec2d b) { const f64 vx = b.x - a.x, vy = b.y - a.y; const f64 uz2 = vx * vx + vy * vy; f64 t = uz2 > 1e-12 ? ((p.x - a.x) * vx + (p.y - a.y) * vy) / uz2 : 0.0; t = std::clamp(t, 0.0, 1.0); return std::hypot(p.x - (a.x + vx * t), p.y - (a.y + vy * t)); } const char* yonAdi(geometri::Gorunum y) { switch (y) { case geometri::Gorunum::On: return "Ön"; case geometri::Gorunum::Arka: return "Arka"; case geometri::Gorunum::Ust: return "Üst"; case geometri::Gorunum::Alt: return "Alt"; case geometri::Gorunum::Sol: return "Sol"; case geometri::Gorunum::Sag: return "Sağ"; default: return "İzometrik"; } } constexpr geometri::Gorunum kYonler[] = { geometri::Gorunum::On, geometri::Gorunum::Arka, geometri::Gorunum::Ust, geometri::Gorunum::Alt, geometri::Gorunum::Sol, geometri::Gorunum::Sag, geometri::Gorunum::Izometrik}; constexpr pafta::SayfaBoyu kSayfalar[] = { pafta::SayfaBoyu::A4Dikey, pafta::SayfaBoyu::A4Yatay, pafta::SayfaBoyu::A3Yatay, pafta::SayfaBoyu::A2Yatay, pafta::SayfaBoyu::A1Yatay, pafta::SayfaBoyu::A0Yatay}; constexpr pafta::Olcek kOlcekler[] = {{1, 1}, {1, 2}, {1, 5}, {1, 10}, {1, 20}, {1, 50}, {2, 1}, {5, 1}, {10, 1}}; // Sağ-tık bağlam menüsü — DM: buton yok, niyet hedeften (görünüm mü boş sayfa mı). void baglamMenusu(UygulamaBaglam& b, PaftaUIDurum& durum, pafta::Pafta& p) { if (!ImGui::BeginPopup("##paftaMenu")) return; pafta::Gorunum* g = durum.menuGorunum ? p.gorunumBul(durum.menuGorunum) : nullptr; if (pafta::Olcu* o = durum.menuOlcu ? p.olcuBul(durum.menuOlcu) : nullptr) { ImGui::TextDisabled("Ölçü"); ImGui::Separator(); char tampon[64]; std::snprintf(tampon, sizeof tampon, "%s", o->sonEk.c_str()); ImGui::SetNextItemWidth(160); if (ImGui::InputText("Tolerans", tampon, sizeof tampon)) { o->sonEk = tampon; durum.menuDegisti = true; } if (ImGui::MenuItem("Değeri elle yaz", nullptr, !o->otoDeger)) { o->otoDeger = !o->otoDeger; if (!o->otoDeger) o->elleDeger = pafta::olcuDegeri(*o, p.olcek); durum.menuDegisti = true; } if (!o->otoDeger) { ImGui::SetNextItemWidth(160); if (ImGui::InputDouble("Değer", &o->elleDeger, 0.0, 0.0, "%.3f")) durum.menuDegisti = true; } ImGui::Separator(); if (ImGui::MenuItem("Ölçüyü sil")) { p.olcuSil(durum.menuOlcu); durum.menuDegisti = true; if (durum.seciliOlcu == durum.menuOlcu) durum.seciliOlcu = 0; durum.menuOlcu = 0; } ImGui::EndPopup(); return; } if (g) { ImGui::TextDisabled("%s", g->ad.empty() ? "Görünüm" : g->ad.c_str()); ImGui::Separator(); if (ImGui::BeginMenu("Yön")) { for (geometri::Gorunum y : kYonler) if (ImGui::MenuItem(yonAdi(y), nullptr, g->yon == y)) { g->yon = y; durum.menuDegisti = true; } // imza değişir → yeniden üretim ImGui::EndMenu(); } if (ImGui::MenuItem("Gizli çizgiler", nullptr, g->gizliCizgi)) { g->gizliCizgi = !g->gizliCizgi; durum.menuDegisti = true; } if (!g->detayMi && paftaPmiGoster()) if (ImGui::MenuItem("PMI göster", nullptr, g->pmiGoster)) { g->pmiGoster = !g->pmiGoster; durum.menuDegisti = true; } if (ImGui::BeginMenu("Ölçek")) { if (ImGui::MenuItem("Paftadan miras", nullptr, g->olcekMiras)) { g->olcekMiras = true; durum.menuDegisti = true; } ImGui::Separator(); for (const pafta::Olcek& o : kOlcekler) if (ImGui::MenuItem(o.metin().c_str(), nullptr, !g->olcekMiras && g->olcek == o)) { g->olcekMiras = false; g->olcek = o; durum.menuDegisti = true; } ImGui::EndMenu(); } if (!g->kesitMi && ImGui::MenuItem("Kesit al (A-A)")) { // iki tık: kesit çizgisi durum.kesitAna = durum.menuGorunum; durum.kesitFaz = 1; durum.olcuFaz = 0; } if (g->kesitMi && ImGui::MenuItem("Tarama", nullptr, g->taramaGoster)) { g->taramaGoster = !g->taramaGoster; durum.menuDegisti = true; } if (!g->detayMi && ImGui::MenuItem("Detay al (büyüteç)")) { // merkez tıkı + yarıçap tıkı durum.detayAna = durum.menuGorunum; durum.detayFaz = 1; durum.olcuFaz = 0; durum.kesitFaz = 0; } if (ImGui::BeginMenu("Ölçü ekle")) { // DM: iki tık nokta, üçüncü tık ofset const std::pair tipler[] = { {"Doğrusal", pafta::OlcuTip::Dogrusal}, {"Yatay", pafta::OlcuTip::Yatay}, {"Dikey", pafta::OlcuTip::Dikey}, {"Çap", pafta::OlcuTip::Capsal}, {"Yarıçap", pafta::OlcuTip::Yaricap}, {"Açı", pafta::OlcuTip::Aci}}; for (const auto& [ad, tip] : tipler) if (ImGui::MenuItem(ad)) { durum.olcuTip = tip; durum.olcuGorunum = durum.menuGorunum; durum.olcuFaz = 1; durum.seciliOlcu = 0; } ImGui::EndMenu(); } ImGui::Separator(); if (ImGui::MenuItem("Görünümü sil")) { durum.onbellek.sil(durum.menuGorunum); p.gorunumSil(durum.menuGorunum); durum.menuDegisti = true; if (durum.seciliGorunum == durum.menuGorunum) durum.seciliGorunum = 0; durum.seciliKume.erase(durum.menuGorunum); durum.menuGorunum = 0; } } else { ImGui::TextDisabled("Sayfa"); ImGui::Separator(); if (ImGui::BeginMenu("Sayfa boyu")) { for (pafta::SayfaBoyu sb : kSayfalar) if (ImGui::MenuItem(pafta::sayfaAdi(sb), nullptr, p.sayfa == sb)) { p.sayfa = sb; durum.menuDegisti = true; } ImGui::EndMenu(); } if (ImGui::BeginMenu("Pafta ölçeği")) { for (const pafta::Olcek& o : kOlcekler) if (ImGui::MenuItem(o.metin().c_str(), nullptr, p.olcek == o)) { p.olcek = o; durum.menuDegisti = true; } ImGui::EndMenu(); } if (ImGui::MenuItem("Antet", nullptr, p.antet.goster)) { p.antet.goster = !p.antet.goster; durum.menuDegisti = true; } ImGui::Separator(); if (ImGui::MenuItem("Sayfayı sığdır")) { durum.sayfaPanX = durum.sayfaPanY = 0.0f; durum.sayfaZoom = 1.0f; } ImGui::Separator(); if (ImGui::MenuItem("DXF olarak dışa aktar…")) durum.dxfIstek = true; ImGui::Separator(); if (ImGui::MenuItem("Teknik Resimden Çık") && b.paftaCik) b.paftaCik(); } ImGui::EndPopup(); } } // namespace void paftaCizVeIsle(UygulamaBaglam& b, PaftaUIDurum& durum, const PaftaCizimBaglam& cb) { if (!durum.modda() || !cb.ciz || !cb.io) return; pafta::Pafta* p = paftaVerisi(b.sahne.dunya(), durum.aktifPafta); if (!p) return; // düğüm silinmiş → çizecek bir şey yok (çıkış komutu ayrı) ImDrawList* ciz = cb.ciz; ImGuiIO& io = *cb.io; const pafta::SayfaOlcu olcu = p->olcu(); // Undo kaydı: ÖNCE kopyası (duzenOncesi) + mevcut durum → KomutYigini (Ctrl+Z). const auto duzenBaslat = [&] { durum.duzenOncesi = std::make_shared(*p); }; const auto duzenKaydet = [&](const char* ad) { if (!durum.duzenOncesi) return; b.cekirdek.uygula(std::make_unique(durum.aktifPafta, std::move(*durum.duzenOncesi), *p, ad)); durum.duzenOncesi.reset(); }; // Biten worker sonuçlarını topla (main-thread; çizimden ÖNCE → aynı karede görünür). for (auto it = durum.beklenenler.begin(); it != durum.beklenenler.end();) { if (!it->bitti->load(std::memory_order_acquire)) { ++it; continue; } durum.uretimde.erase(it->gorunumKimlik); if (it->sonuc->gecerli) { if (pafta::Gorunum* gg = p->gorunumBul(it->gorunumKimlik)) gg->boyut = it->sonuc->boyut; durum.onbellek.ata(it->gorunumKimlik, *it->sonuc); } it = durum.beklenenler.erase(it); } // ---- Etkileşim: tekerlek zoom · orta tuş pan (sürükleme aşağıda) ---- if (cb.vpHover) { if (io.MouseWheel != 0.0f) { const f32 adim = std::pow(std::max(1.001f, paftaZoomAdim()), io.MouseWheel); durum.sayfaZoom = std::clamp(durum.sayfaZoom * adim, 0.1f, 20.0f); kareIste(2); } if (ImGui::IsMouseDragging(ImGuiMouseButton_Middle)) { durum.sayfaPanX += io.MouseDelta.x; durum.sayfaPanY += io.MouseDelta.y; kareIste(2); } if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Middle)) { // sayfayı sığdır durum.sayfaPanX = durum.sayfaPanY = 0.0f; durum.sayfaZoom = 1.0f; kareIste(2); } } const pafta::SayfaDonusum don = pafta::sayfaDonusum(olcu, cb.vpBoyutX, cb.vpBoyutY, {durum.sayfaPanX, durum.sayfaPanY}, durum.sayfaZoom, paftaKenarPayiPx()); const ImVec2 kok{cb.vpRectMinX, cb.vpRectMinY}; const auto ekr = [&](Vec2d sayfa) { const ImVec2 e = iv(don, sayfa); return ImVec2{kok.x + e.x, kok.y + e.y}; }; // ---- Sayfa (kağıt + gölge + çerçeve + antet) ---- const ImVec2 solAlt = ekr({0, 0}); const ImVec2 sagUst = ekr({olcu.en, olcu.boy}); ciz->AddRectFilled({solAlt.x + 6, sagUst.y + 6}, {sagUst.x + 6, solAlt.y + 6}, GOLGE, 2.0f); ciz->AddRectFilled({solAlt.x, sagUst.y}, {sagUst.x, solAlt.y}, KAGIT, 2.0f); ciz->AddRect({solAlt.x, sagUst.y}, {sagUst.x, solAlt.y}, CERCEVE, 2.0f, 0, 1.5f); { // çizim alanı çerçevesi (ISO kenar payı ~10mm) const ImVec2 a = ekr({10, 10}), c = ekr({olcu.en - 10, olcu.boy - 10}); ciz->AddRect({a.x, c.y}, {c.x, a.y}, SOLUK, 0.0f, 0, 1.0f); } if (p->antet.goster) { const ImVec2 a = ekr({olcu.en - 10 - p->antet.en, 10}); const ImVec2 c = ekr({olcu.en - 10, 10 + p->antet.boy}); ciz->AddRectFilled({a.x, c.y}, {c.x, a.y}, IM_COL32(255, 255, 255, 200)); ciz->AddRect({a.x, c.y}, {c.x, a.y}, CERCEVE, 0.0f, 0, 1.2f); const std::string satir1 = p->antet.parca.empty() ? p->ad : p->antet.parca; const std::string satir2 = "Ölçek " + p->olcek.metin() + " " + pafta::sayfaAdi(p->sayfa) + (p->antet.cizen.empty() ? "" : (" " + p->antet.cizen)); ciz->AddText({a.x + 8, c.y + 6}, CERCEVE, satir1.c_str()); ciz->AddText({a.x + 8, c.y + 24}, SOLUK, satir2.c_str()); } // ---- Görünümler: önbellekten çiz, eksik/bayat olanı WORKER'a kuyrukla ---- std::vector nok; // yeniden kullanılan tampon (kare başına tahsis yok) for (pafta::Gorunum& g : p->gorunumler()) { if (!g.goster) continue; const Dugum* kaynak = g.kaynak ? b.sahne.bul(g.kaynak) : nullptr; const geometri::BrepGovde* govde = (kaynak && kaynak->brep) ? kaynak->brep.get() : nullptr; const pafta::Olcek olcek = p->etkinOlcek(g); u64 imza = pafta::cizimImzasi(govde, g.yon, olcek); if (g.kesitMi) imza = pafta::kesitImzasi(imza, g.kesitA, g.kesitB); if (g.detayMi) { // Detay: ANA görünümün çiziminden türetilir (saf 2B kırpma — OCCT yok, main'de ucuz). // İmza ana imza + daire + ölçek → ana değişince/daire oynayınca yeniden kırpılır. const pafta::Gorunum* ana = p->gorunumBul(g.detayAnaGorunum); const pafta::GorunumCizim* anaCizim = ana ? durum.onbellek.bulHamm(ana->kimlik) : nullptr; if (ana && anaCizim && anaCizim->gecerli) { u64 di = pafta::kesitImzasi(anaCizim->imza, g.detayMerkez, {g.detayYaricap, 0}); di = pafta::kesitImzasi(di, {(f64)olcek.pay, (f64)olcek.payda}, {0, 0}); if (!durum.onbellek.bul(g.kimlik, di)) { pafta::GorunumCizim dc = pafta::detayCizimUret( *anaCizim, p->etkinOlcek(*ana), g.detayMerkez, g.detayYaricap, olcek); dc.imza = di; if (pafta::Gorunum* gg = p->gorunumBul(g.kimlik)) gg->boyut = dc.boyut; durum.onbellek.ata(g.kimlik, std::move(dc)); } imza = di; } } const pafta::GorunumCizim* cizim = durum.onbellek.bul(g.kimlik, imza); if (!cizim && govde && !g.detayMi && !durum.uretimde.count(g.kimlik)) { durum.uretimde.insert(g.kimlik); auto brep = kaynak->brep; // shared_ptr KOPYASI: worker boyunca gövde yaşar auto sonuc = std::make_shared(); auto bitti = std::make_shared>(false); const geometri::Gorunum yon = g.yon; // Kesit görünümü: kesme düzlemi ANA görünümün yerel uzayından türetilir // (ucuz, OCCT'siz — main-thread'de hesaplanır), kesme worker'da yapılır. pafta::KesitDuzlem kd; if (g.kesitMi) { const pafta::Gorunum* ana = p->gorunumBul(g.kesitAnaGorunum); const pafta::GorunumCizim* anaCizim = ana ? durum.onbellek.bulHamm(ana->kimlik) : nullptr; if (ana && anaCizim) kd = pafta::kesitDuzlemi(*anaCizim, ana->yon, p->etkinOlcek(*ana), g.kesitA, g.kesitB); if (!kd.gecerli) { durum.uretimde.erase(g.kimlik); continue; } // ana çizim hazır değil } durum.beklenenler.push_back({g.kimlik, sonuc, bitti}); b.cekirdek.gorevler().kuyrukla( "Teknik resim görünümü", [brep, sonuc, bitti, yon, olcek, kd](GorevBaglam&) { // WORKER: OCCT (ağır) *sonuc = kd.gecerli ? pafta::kesitGorunumUret(*brep, yon, olcek, kd.nokta, kd.normal) : pafta::gorunumUret(*brep, yon, olcek); bitti->store(true, std::memory_order_release); kareIste(2); }, [] {}); // finalize BOŞ: sonuç paylaşımlı kutuda, çizim toplar (dangling yok) } if (!cizim) cizim = durum.onbellek.bulHamm(g.kimlik); // bayat da olsa çiz (boş kare yok) const bool secili = durum.seciliKume.count(g.kimlik) != 0; if (cizim && cizim->gecerli) { const f32 kal = std::max(0.5f, paftaCizgiKalinlik()); const auto polyCiz = [&](const std::vector>& gruplar, ImU32 renk, bool kesikli) { for (const auto& pl : gruplar) { if (pl.size() < 2) continue; nok.clear(); nok.reserve(pl.size()); for (const Vec2d& q : pl) nok.push_back(ekr({g.konum.x + q.x, g.konum.y + q.y})); if (kesikli) kesikliCiz(ciz, nok, renk, kal * 0.9f, 4.0f); else ciz->AddPolyline(nok.data(), (int)nok.size(), renk, 0, kal); } }; // Kesit taraması (hatch) — kesme YÜZÜ konturlarından, önbellekli. if (g.kesitMi && g.taramaGoster && !cizim->kesitKonturlari.empty()) { auto& tk = durum.taramaOnbellek[g.kimlik]; if (tk.imza != cizim->imza || tk.aci != paftaTaramaAci() || tk.aralik != paftaTaramaAralikMm()) { tk.imza = cizim->imza; tk.aci = paftaTaramaAci(); tk.aralik = paftaTaramaAralikMm(); tk.cizgiler = pafta::taramaUret(cizim->kesitKonturlari, tk.aci, tk.aralik); } for (const auto& [ta, tb] : tk.cizgiler) ciz->AddLine(ekr({g.konum.x + ta.x, g.konum.y + ta.y}), ekr({g.konum.x + tb.x, g.konum.y + tb.y}), SOLUK, kal * 0.7f); } if (g.gizliCizgi) polyCiz(cizim->gizli, GIZLI, true); polyCiz(cizim->gorunur, secili ? SECILI : CIZGI, false); } // Çerçeve + etiket: seçiliyken belirgin, değilken soluk (DM: kutu değil ipucu). const f64 yariEn = std::max(4.0, g.boyut.x * 0.5), yariBoy = std::max(4.0, g.boyut.y * 0.5); const ImVec2 a = ekr({g.konum.x - yariEn, g.konum.y - yariBoy}); const ImVec2 c = ekr({g.konum.x + yariEn, g.konum.y + yariBoy}); if (secili) ciz->AddRect({a.x - 4, c.y - 4}, {c.x + 4, a.y + 4}, SECILI, 2.0f, 0, 1.2f); // Hizalama kilidi GÖRSEL işareti ("kalan cila"): eksen-kilitli görünüm SEÇİLİYKEN // ebeveyne kesikli bağ + kilit/eksen simgesi — kullanıcı görünümün neden tek // eksende kaydığını görür. Bağımsız/ebeveynsiz görünümde çizilmez (gürültü yok). if (secili && g.ust != 0 && g.hiza != pafta::Gorunum::Hiza::Bagimsiz) { if (const pafta::Gorunum* eb = p->gorunumBul(g.ust)) { const ImVec2 m0 = ekr(g.konum), m1 = ekr(eb->konum); std::vector bag = {m0, m1}; kesikliCiz(ciz, bag, SOLUK, 1.0f, 6.0f); const char* et = g.hiza == pafta::Gorunum::Hiza::Yatay ? "⚭ ↔" : "⚭ ↕"; ciz->AddText({(m0.x + m1.x) * 0.5f + 4.0f, (m0.y + m1.y) * 0.5f - 14.0f}, SOLUK, et); } } if (!g.ad.empty()) ciz->AddText({a.x, a.y + 4}, secili ? SECILI : SOLUK, g.ad.c_str()); if (!cizim && govde) ciz->AddText({a.x, c.y - 16}, SOLUK, "üretiliyor…"); } // İmleç altındaki ölçü (görünüm-yerel mesafe; ölçü çizgisi + uzantılar). const auto olcuVur = [&](Vec2d sayfaNoktasi, f64 tolMm) -> u32 { for (auto it = p->olculer().rbegin(); it != p->olculer().rend(); ++it) { if (!it->goster) continue; const pafta::Gorunum* sahip = it->gorunum ? p->gorunumBul(it->gorunum) : nullptr; const Vec2d tas = sahip ? sahip->konum : Vec2d{0, 0}; const Vec2d yer{sayfaNoktasi.x - tas.x, sayfaNoktasi.y - tas.y}; const pafta::OlcuCizim oc = pafta::olcuGeometrisi(*it); if (!oc.gecerli) continue; for (const auto& [a, c2] : oc.cizgiler) if (parcayaUzaklik(yer, a, c2) <= tolMm) return it->kimlik; } return 0; }; // ---- Sol tuş: ölçü seçimi (öncelikli) → görünüm sürükleme ---- if (cb.vpHover && durum.olcuFaz == 0 && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { const Vec2d sf = don.sayfaya({io.MousePos.x - kok.x, io.MousePos.y - kok.y}); const f64 tolMm = don.olcek > 1e-9 ? paftaTutunmaPx() / don.olcek : 1.0; const u32 ov = olcuVur(sf, tolMm); if (ov) { // ölçü tıklandı: sürükleme = ofset değişimi durum.seciliOlcu = ov; durum.olcuSurukle = ov; duzenBaslat(); durum.seciliGorunum = 0; durum.surukleGorunum = 0; } else { durum.seciliOlcu = 0; const u32 vur = pafta::gorunumVur(*p, sf, paftaVurToleransMm()); // Ctrl+tık = kümeye ekle/çıkar (DM çoklu seçim); düz tık = tekil küme. if (io.KeyCtrl && vur) { if (!durum.seciliKume.erase(vur)) durum.seciliKume.insert(vur); durum.seciliGorunum = vur; } else { durum.seciliKume.clear(); if (vur) durum.seciliKume.insert(vur); durum.seciliGorunum = vur; } durum.surukleGorunum = durum.seciliKume.count(vur) ? vur : 0; if (const pafta::Gorunum* g = durum.surukleGorunum ? p->gorunumBul(vur) : nullptr) { durum.surukleKapma = {sf.x - g->konum.x, sf.y - g->konum.y}; duzenBaslat(); } } kareIste(2); } if (durum.olcuSurukle) { // ofset = imlecin dayanak doğrultusuna dik uzaklığı if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) { pafta::Olcu* o = p->olcuBul(durum.olcuSurukle); const pafta::Gorunum* sahip = o && o->gorunum ? p->gorunumBul(o->gorunum) : nullptr; if (o) { const Vec2d sf = don.sayfaya({io.MousePos.x - kok.x, io.MousePos.y - kok.y}); const Vec2d tas = sahip ? sahip->konum : Vec2d{0, 0}; const Vec2d yer{sf.x - tas.x, sf.y - tas.y}; const Vec2d orta{(o->p1.x + o->p2.x) * 0.5, (o->p1.y + o->p2.y) * 0.5}; const Vec2d d2{o->p2.x - o->p1.x, o->p2.y - o->p1.y}; const f64 uz = std::hypot(d2.x, d2.y); const Vec2d n = uz > 1e-9 ? Vec2d{-d2.y / uz, d2.x / uz} : Vec2d{0, 1}; o->ofset = (yer.x - orta.x) * n.x + (yer.y - orta.y) * n.y; kareIste(2); } } else { duzenKaydet("Ölçü Taşı"); durum.olcuSurukle = 0; } } // ---- Ölçüler: çizim + seçim ---- const f64 okBoy = std::max(0.5, paftaOkBoyuMm()); const auto olcuCiz = [&](const pafta::Olcu& o, const pafta::Gorunum* sahip, ImU32 renk) { const pafta::OlcuCizim oc = pafta::olcuGeometrisi(o); if (!oc.gecerli) return; const Vec2d tas = sahip ? sahip->konum : Vec2d{0, 0}; const auto yerel = [&](Vec2d q) { return ekr({tas.x + q.x, tas.y + q.y}); }; for (const auto& [a, c2] : oc.cizgiler) ciz->AddLine(yerel(a), yerel(c2), renk, 1.0f); // Ok başları: ölçü çizgisi yönünde içe bakan küçük üçgenler. const Vec2d dik{-oc.okYon.y, oc.okYon.x}; const auto okCiz = [&](Vec2d uc, f64 isaret) { const Vec2d taban{uc.x + oc.okYon.x * okBoy * isaret, uc.y + oc.okYon.y * okBoy * isaret}; ciz->AddTriangleFilled(yerel(uc), yerel({taban.x + dik.x * okBoy * 0.3, taban.y + dik.y * okBoy * 0.3}), yerel({taban.x - dik.x * okBoy * 0.3, taban.y - dik.y * okBoy * 0.3}), renk); }; okCiz(oc.okA, 1.0); okCiz(oc.okB, -1.0); const std::string metin = pafta::olcuMetni(o, sahip ? p->etkinOlcek(*sahip) : p->olcek); const ImVec2 mp = yerel(oc.metinCipa); // Metin boyu SAYFA mm cinsinden (ISO teknik resim: zoom'da sayfayla ölçeklenir; // eski px-sabit davranış zoom'da metni sayfaya oranla küçültüyordu — "kalan cila"). // pafta.yazi_sayfa_olcekli kapalıysa eski davranış birebir. const f32 boyPx = paftaYaziSayfaOlcekli() ? paftaYaziMm() * (f32)don.olcek : 0.0f; const ImVec2 mb = boyPx > 0.0f ? ImGui::GetFont()->CalcTextSizeA(boyPx, FLT_MAX, 0.0f, metin.c_str()) : ImGui::CalcTextSize(metin.c_str()); // Metin ölçü çizgisi doğrultusunda (ISO 129): VtxBuffer 2×2 döndürme (donukMetinCiz). // Çıpa metnin ALT kenarı ölçü çizgisine değecek şekilde dik yönde kaydırılır. const f32 aci = (f32)oc.metinAci; const f32 dikX = std::sin(aci), dikY = std::cos(aci); // ekran uzayında "yukarı" donukMetinCiz(ciz, {mp.x - dikX * (mb.y * 0.5f + 2.0f), mp.y - dikY * (mb.y * 0.5f + 2.0f)}, aci, renk, metin.c_str(), boyPx); }; for (const pafta::Olcu& o : p->olculer()) { if (!o.goster) continue; olcuCiz(o, o.gorunum ? p->gorunumBul(o.gorunum) : nullptr, durum.seciliOlcu == o.kimlik ? SECILI : CIZGI); } // ---- Kesit çizgileri: ANA görünümün üstüne (kalın kesikli + oklar + harf) ---- const auto kesitCizgiCiz = [&](const pafta::Gorunum& ana, Vec2d a, Vec2d c2, char harf, ImU32 renk) { const ImVec2 pa = ekr({ana.konum.x + a.x, ana.konum.y + a.y}); const ImVec2 pb = ekr({ana.konum.x + c2.x, ana.konum.y + c2.y}); std::vector iki = {pa, pb}; kesikliCiz(ciz, iki, renk, std::max(1.0f, paftaCizgiKalinlik() * 1.6f), 9.0f); // Uçlarda bakış yönünü gösteren oklar (çizgiye dik). const f64 dx = c2.x - a.x, dy = c2.y - a.y; const f64 uz = std::hypot(dx, dy); if (uz > 1e-9) { const Vec2d n{-dy / uz, dx / uz}; const f64 ok = std::max(1.0, paftaOkBoyuMm()) * 2.0; for (const Vec2d& uc : {a, c2}) { const ImVec2 p0 = ekr({ana.konum.x + uc.x, ana.konum.y + uc.y}); const ImVec2 p1 = ekr({ana.konum.x + uc.x + n.x * ok, ana.konum.y + uc.y + n.y * ok}); ciz->AddLine(p0, p1, renk, 1.5f); } } const std::string et = pafta::kesitEtiketi(harf); ciz->AddText({pa.x - 6, pa.y - 16}, renk, et.c_str()); ciz->AddText({pb.x - 6, pb.y + 4}, renk, et.c_str()); }; for (const pafta::Gorunum& g : p->gorunumler()) { if (!g.kesitMi || !g.goster) continue; const pafta::Gorunum* ana = p->gorunumBul(g.kesitAnaGorunum); if (ana && ana->goster) kesitCizgiCiz(*ana, g.kesitA, g.kesitB, g.kesitHarf, CERCEVE); } // Detay işaretleri: ana görünümde kaynak daire + harf; detay görünümünde sınır çemberi. for (const pafta::Gorunum& g : p->gorunumler()) { if (!g.detayMi || !g.goster) continue; const f64 pxR = don.olcek; // mm→px if (const pafta::Gorunum* ana = p->gorunumBul(g.detayAnaGorunum); ana && ana->goster) { const ImVec2 m = ekr({ana->konum.x + g.detayMerkez.x, ana->konum.y + g.detayMerkez.y}); ciz->AddCircle(m, (f32)(g.detayYaricap * pxR), CERCEVE, 0, 1.4f); char et[4] = {g.detayHarf, 0}; ciz->AddText({m.x + (f32)(g.detayYaricap * pxR) + 3, m.y - 14}, CERCEVE, et); } const ImVec2 dm = ekr({g.konum.x, g.konum.y}); ciz->AddCircle(dm, (f32)(g.boyut.x * 0.5 * pxR), SOLUK, 0, 1.0f); char et2[32]; std::snprintf(et2, sizeof et2, "%c (%s)", g.detayHarf, p->etkinOlcek(g).metin().c_str()); ciz->AddText({dm.x - 16, dm.y - (f32)(g.boyut.y * 0.5 * pxR) - 18}, CERCEVE, et2); } // ---- PMI yansıtma (3B FTA → pafta, TÜRETİLMİŞ): defterden canlı çizilir — // serileşme yok, PMI değişince pafta kendiliğinden güncel. Kesit görünümü DAHİL: // kesitGorunumUret bölünmüş gövdeyi AYNI yon ekseniyle izdüşürür + hamMerkez çizimden // okunur → çapa zinciri birebir tutarlı (kesilip atılan taraftaki PMI da çizilir — // arka yüz MVP notuyla aynı sınıf). Detay görünümü MVP dışı (2B kırpma+büyütme ayrı // uzay). pafta.pmi_goster kapalı = eski birebir (hiç çizilmez). if (paftaPmiGoster()) { const f32 pmiBoyPx = paftaYaziSayfaOlcekli() ? paftaYaziMm() * (f32)don.olcek : 0.0f; for (const pafta::Gorunum& g : p->gorunumler()) { if (!g.goster || !g.pmiGoster || !g.kaynak || g.detayMi) continue; const pafta::GorunumCizim* cizim = durum.onbellek.bulHamm(g.kimlik); if (!cizim || !cizim->gecerli) continue; const Dugum* kaynak = b.sahne.bul(g.kaynak); if (!kaynak) continue; const Donusumd ters = kaynak->donusum.ters(); const pafta::Olcek olcek = p->etkinOlcek(g); for (const pmi::PmiOge& o : b.pmiSis.defter().hepsi()) { if (!o.goster || !o.gecerli) continue; bool ait = false; for (const pmi::PmiHedef& h : o.hedefler) if (h.govde == g.kaynak) { ait = true; break; } if (!ait) continue; const std::string metin = pmi::ogeMetni(o); if (metin.empty()) continue; // Çapa dünya → gövde-yerel (pafta izdüşümü gövde-yerel brep'ten) → görünüm-yerel. const Vec3d yerel3 = ters.noktaUygula({(f64)o.capa.x, (f64)o.capa.y, (f64)o.capa.z}); const Vec2d y2 = pafta::nokta3dYerele(*cizim, g.yon, yerel3, olcek); const ImVec2 capaEk = ekr({g.konum.x + y2.x, g.konum.y + y2.y}); const ImU32 renk = IM_COL32((int)(o.stil.renk.x * 255), (int)(o.stil.renk.y * 255), (int)(o.stil.renk.z * 255), 255); // Kutu çapadan sayfa-mm sabit ofsette (sağ-üst; sayfa +y yukarı → ekranda üste // düşer); lider çizgisi + çapa noktası. FCF hücreleri MVP'de tek dikdörtgen. const ImVec2 kutuKok = ekr({g.konum.x + y2.x + 8.0, g.konum.y + y2.y + 8.0}); const ImVec2 mb = pmiBoyPx > 0.0f ? ImGui::GetFont()->CalcTextSizeA(pmiBoyPx, FLT_MAX, 0.0f, metin.c_str()) : ImGui::CalcTextSize(metin.c_str()); ciz->AddCircleFilled(capaEk, 2.0f, renk); ciz->AddLine(capaEk, kutuKok, renk, 1.0f); const f32 pad = 3.0f; ciz->AddRect({kutuKok.x - pad, kutuKok.y - pad - mb.y}, {kutuKok.x + mb.x + pad, kutuKok.y + pad}, renk, 0.0f, 0, 1.1f); if (pmiBoyPx > 0.0f) ciz->AddText(ImGui::GetFont(), pmiBoyPx, {kutuKok.x, kutuKok.y - mb.y}, renk, metin.c_str()); else ciz->AddText({kutuKok.x, kutuKok.y - mb.y}, renk, metin.c_str()); } } } // ---- Ölçü yerleştirme (bağlam menüsünden başlar): tutunma → iki nokta → ofset ---- Vec2d imlecSayfa{0, 0}; bool imlecGecerli = false; if (cb.vpHover) { imlecSayfa = don.sayfaya({io.MousePos.x - kok.x, io.MousePos.y - kok.y}); imlecGecerli = true; } if (durum.olcuFaz > 0 && imlecGecerli) { pafta::Gorunum* hg = p->gorunumBul(durum.olcuGorunum); const Dugum* kd = hg && hg->kaynak ? b.sahne.bul(hg->kaynak) : nullptr; const pafta::GorunumCizim* hc = hg ? durum.onbellek.bulHamm(hg->kimlik) : nullptr; if (!hg || !hc) { durum.olcuFaz = 0; // görünüm/çizim yok → sessiz iptal } else { const pafta::Olcek hOlcek = p->etkinOlcek(*hg); // Tutunma adayları ÖNBELLEKLİ: her kare BREP taraması yasak (sıcak yol). auto& kayit = durum.tutunmaOnbellek[hg->kimlik]; const u64 imza2 = pafta::cizimImzasi(kd && kd->brep ? kd->brep.get() : nullptr, hg->yon, hOlcek); if (kayit.first != imza2 && kd && kd->brep) { kayit.first = imza2; kayit.second = pafta::tutunmaAdaylari(*kd->brep, hg->yon, *hc, hOlcek); } const Vec2d yerelImlec{imlecSayfa.x - hg->konum.x, imlecSayfa.y - hg->konum.y}; const f64 tolMm = don.olcek > 1e-9 ? paftaTutunmaPx() / don.olcek : 1.0; Vec2d yakalanan = yerelImlec; const bool snap = pafta::tutunmaYakala(kayit.second, yerelImlec, tolMm, yakalanan); if (snap) { // snap işareti (küçük kare) const ImVec2 sp = ekr({hg->konum.x + yakalanan.x, hg->konum.y + yakalanan.y}); ciz->AddRect({sp.x - 4, sp.y - 4}, {sp.x + 4, sp.y + 4}, SECILI, 0.0f, 0, 1.5f); } // Önizleme pafta::Olcu on; on.tip = durum.olcuTip; on.p1 = durum.olcuFaz >= 2 ? durum.olcuP1 : yakalanan; on.p2 = durum.olcuFaz >= 3 ? durum.olcuP2 : yakalanan; if (durum.olcuFaz == 3) { const Vec2d orta{(durum.olcuP1.x + durum.olcuP2.x) * 0.5, (durum.olcuP1.y + durum.olcuP2.y) * 0.5}; const Vec2d d2{durum.olcuP2.x - durum.olcuP1.x, durum.olcuP2.y - durum.olcuP1.y}; const f64 uz = std::hypot(d2.x, d2.y); const Vec2d n = uz > 1e-9 ? Vec2d{-d2.y / uz, d2.x / uz} : Vec2d{0, 1}; on.ofset = (yerelImlec.x - orta.x) * n.x + (yerelImlec.y - orta.y) * n.y; } if (durum.olcuFaz >= 2) olcuCiz(on, hg, SECILI); if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { if (durum.olcuFaz == 1) { durum.olcuP1 = yakalanan; durum.olcuFaz = 2; } else if (durum.olcuFaz == 2) { durum.olcuP2 = yakalanan; durum.olcuFaz = 3; } else { pafta::Olcu yeni = on; yeni.gorunum = hg->kimlik; yeni.p1 = durum.olcuP1; yeni.p2 = durum.olcuP2; duzenBaslat(); durum.seciliOlcu = p->olcuEkle(yeni); duzenKaydet("Ölçü Ekle"); durum.olcuFaz = 0; } kareIste(2); } } } // ---- Kesit alma (bağlam menüsü → iki tık): çizgi ana görünümün üstünde ---- if (durum.kesitFaz > 0 && imlecGecerli) { pafta::Gorunum* ana = p->gorunumBul(durum.kesitAna); if (!ana) { durum.kesitFaz = 0; } else { const Vec2d yerel{imlecSayfa.x - ana->konum.x, imlecSayfa.y - ana->konum.y}; if (durum.kesitFaz == 1) { const ImVec2 sp = ekr(imlecSayfa); ciz->AddLine({sp.x - 8, sp.y}, {sp.x + 8, sp.y}, SECILI, 1.0f); ciz->AddLine({sp.x, sp.y - 8}, {sp.x, sp.y + 8}, SECILI, 1.0f); } else { kesitCizgiCiz(*ana, durum.kesitP1, yerel, pafta::sonrakiKesitHarfi(*p), SECILI); } if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { if (durum.kesitFaz == 1) { durum.kesitP1 = yerel; durum.kesitFaz = 2; } else { pafta::Gorunum kg; kg.kesitMi = true; kg.kesitAnaGorunum = ana->kimlik; kg.kesitA = durum.kesitP1; kg.kesitB = yerel; kg.kesitHarf = pafta::sonrakiKesitHarfi(*p); kg.kaynak = ana->kaynak; kg.yon = pafta::kesitGorunumYonu(ana->yon, kg.kesitA, kg.kesitB); kg.ad = pafta::kesitEtiketi(kg.kesitHarf) + " Kesiti"; kg.olcekMiras = true; duzenBaslat(); // Ana görünümün sağına, sayfada kalacak şekilde yerleştir (kullanıcı sürükler). const pafta::SayfaOlcu so = p->olcu(); kg.boyut = ana->boyut; kg.konum = {std::min(so.en - 20.0, ana->konum.x + ana->boyut.x + 40.0), ana->konum.y}; durum.seciliGorunum = p->gorunumEkle(kg); duzenKaydet("Kesit Al"); durum.kesitFaz = 0; } kareIste(2); } } } // ---- Detay alma (merkez tıkı → yarıçap tıkı, canlı önizleme) ---- if (durum.detayFaz > 0 && imlecGecerli) { pafta::Gorunum* ana = p->gorunumBul(durum.detayAna); if (!ana) { durum.detayFaz = 0; } else { const Vec2d yerel{imlecSayfa.x - ana->konum.x, imlecSayfa.y - ana->konum.y}; if (durum.detayFaz == 1) { const ImVec2 sp = ekr(imlecSayfa); ciz->AddCircle(sp, 4.0f, SECILI, 0, 1.5f); } else { const f64 r = std::hypot(yerel.x - durum.detayMerkez.x, yerel.y - durum.detayMerkez.y); const ImVec2 m = ekr({ana->konum.x + durum.detayMerkez.x, ana->konum.y + durum.detayMerkez.y}); ciz->AddCircle(m, (f32)(r * don.olcek), SECILI, 0, 1.5f); } if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { if (durum.detayFaz == 1) { durum.detayMerkez = yerel; durum.detayFaz = 2; } else { const f64 r = std::hypot(yerel.x - durum.detayMerkez.x, yerel.y - durum.detayMerkez.y); if (r > 0.5) { pafta::Gorunum dg; dg.detayMi = true; dg.detayAnaGorunum = ana->kimlik; dg.detayMerkez = durum.detayMerkez; dg.detayYaricap = r; dg.detayHarf = pafta::sonrakiDetayHarfi(*p); dg.kaynak = ana->kaynak; dg.yon = ana->yon; dg.olcekMiras = false; // Varsayılan büyütme: ana ölçeğin 2 katına en yakın standart (ISO detay geleneği). dg.olcek = pafta::standartOlcek(p->etkinOlcek(*ana).oran() * 2.0); dg.ad = std::string(1, dg.detayHarf) + " Detayı"; const pafta::SayfaOlcu so = p->olcu(); dg.boyut = {2 * r, 2 * r}; dg.konum = {std::min(so.en - 20.0, ana->konum.x + ana->boyut.x * 0.5 + 2 * r + 30.0), std::min(so.boy - 20.0, ana->konum.y + ana->boyut.y * 0.5 + r + 20.0)}; duzenBaslat(); durum.seciliGorunum = p->gorunumEkle(dg); duzenKaydet("Detay Al"); } durum.detayFaz = 0; } kareIste(2); } } } // ---- Del: seçili ölçüyü / seçili görünüm KÜMESİNİ sil (undo'lu — DM klavye jesti) ---- if (cb.vpHover && ImGui::IsKeyPressed(ImGuiKey_Delete) && !ImGui::IsAnyItemActive()) { if (durum.seciliOlcu) { duzenBaslat(); p->olcuSil(durum.seciliOlcu); duzenKaydet("Ölçü Sil"); durum.seciliOlcu = 0; kareIste(2); } else if (!durum.seciliKume.empty()) { duzenBaslat(); for (u32 k : durum.seciliKume) { durum.onbellek.sil(k); p->gorunumSil(k); // görünümün ölçüleri + çocuk bağları motor tarafında düşer } duzenKaydet(durum.seciliKume.size() > 1 ? "Görünümleri Sil" : "Görünüm Sil"); durum.seciliKume.clear(); durum.seciliGorunum = 0; kareIste(2); } } // ---- Sayfanın DIŞINA çift tık → moddan çık (ürün ağacı çift-tıkının ikizi) ---- if (cb.vpHover && durum.olcuFaz == 0 && durum.kesitFaz == 0 && durum.detayFaz == 0 && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && imlecGecerli) { const bool sayfaDisi = imlecSayfa.x < 0 || imlecSayfa.y < 0 || imlecSayfa.x > olcu.en || imlecSayfa.y > olcu.boy; if (sayfaDisi && b.paftaCik) { b.paftaCik(); return; // durum kapandı: bu karede başka etkileşim işleme } } // ---- Sağ tık: bağlam menüsü (hedef = imleç altındaki görünüm, yoksa sayfa) ---- if (cb.vpHover && ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { const Vec2d sf = don.sayfaya({io.MousePos.x - kok.x, io.MousePos.y - kok.y}); const f64 tolMm2 = don.olcek > 1e-9 ? paftaTutunmaPx() / don.olcek : 1.0; durum.menuOlcu = olcuVur(sf, tolMm2); durum.menuGorunum = durum.menuOlcu ? 0 : pafta::gorunumVur(*p, sf, paftaVurToleransMm()); if (durum.menuGorunum) durum.seciliGorunum = durum.menuGorunum; durum.menuOncesi = std::make_shared(*p); // undo: menü öncesi durum durum.menuDegisti = false; ImGui::OpenPopup("##paftaMenu"); kareIste(2); } baglamMenusu(b, durum, *p); // Menü kapandıysa ve değişiklik yapıldıysa TEK undo adımı olarak kaydet. if (durum.menuOncesi && !ImGui::IsPopupOpen("##paftaMenu")) { if (durum.menuDegisti) b.cekirdek.uygula(std::make_unique( durum.aktifPafta, std::move(*durum.menuOncesi), *p, "Pafta Düzenle")); durum.menuOncesi.reset(); durum.menuDegisti = false; } // ---- DXF dışa aktarım isteği: dosya seç (main) → üretim + yazım WORKER'da ---- if (durum.dxfIstek) { durum.dxfIstek = false; wchar_t yolW[MAX_PATH] = {}; OPENFILENAMEW ofn{}; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = (HWND)b.pencere.dogalTutamac(); ofn.lpstrFilter = L"DXF (*.dxf)\0*.dxf\0"; ofn.lpstrFile = yolW; ofn.nMaxFile = MAX_PATH; ofn.lpstrDefExt = L"dxf"; ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST; if (GetSaveFileNameW(&ofn)) { const int bayt = WideCharToMultiByte(CP_UTF8, 0, yolW, -1, nullptr, 0, nullptr, nullptr); std::string yol(bayt - 1, 0); WideCharToMultiByte(CP_UTF8, 0, yolW, -1, yol.data(), bayt, nullptr, nullptr); // Snapshot main-thread'de: pafta verisi (saf kopya) + gövde handle'ları + kesit // düzlemleri. Worker sahneye DOKUNMAZ (paylaşımlı-kutu ilkesi; sonuç yalnız disk). auto paftaKopya = std::make_shared(*p); struct UretimGirdi { u32 kimlik; std::shared_ptr brep; geometri::Gorunum yon; pafta::Olcek olcek; bool kesit = false; pafta::KesitDuzlem kd; }; auto girdiler = std::make_shared>(); for (const pafta::Gorunum& g : p->gorunumler()) { if (!g.goster || !g.kaynak) continue; const Dugum* kd2 = b.sahne.bul(g.kaynak); if (!kd2 || !kd2->brep) continue; UretimGirdi u{g.kimlik, kd2->brep, g.yon, p->etkinOlcek(g)}; if (g.kesitMi) { const pafta::Gorunum* ana = p->gorunumBul(g.kesitAnaGorunum); const pafta::GorunumCizim* anaCizim = ana ? durum.onbellek.bulHamm(ana->kimlik) : nullptr; if (ana && anaCizim) { u.kd = pafta::kesitDuzlemi(*anaCizim, ana->yon, p->etkinOlcek(*ana), g.kesitA, g.kesitB); u.kesit = u.kd.gecerli; } } girdiler->push_back(std::move(u)); } auto tamam = std::make_shared>(false); b.cekirdek.gorevler().kuyrukla( "DXF Dışa Aktar", [paftaKopya, girdiler, yol, tamam](GorevBaglam& gb) { // WORKER: HLR + disk std::unordered_map cizimler; size_t i = 0; for (const UretimGirdi& u : *girdiler) { if (gb.iptalIstendi()) return; cizimler[u.kimlik] = u.kesit ? pafta::kesitGorunumUret(*u.brep, u.yon, u.olcek, u.kd.nokta, u.kd.normal) : pafta::gorunumUret(*u.brep, u.yon, u.olcek); gb.ilerlet(0.9f * (f32)(++i) / (f32)girdiler->size(), "Görünüm üretiliyor"); } gb.ilerlet(0.95f, "Yazılıyor"); *tamam = pafta::paftaDxfKaydet(*paftaKopya, cizimler, yol); }, [&b, yol, tamam] { b.bilgiVer(*tamam ? "DXF dışa aktarıldı: " + yol : "DXF yazılamadı: " + yol); }); } } if (durum.surukleGorunum) { if (ImGui::IsMouseDown(ImGuiMouseButton_Left)) { const Vec2d sf = don.sayfaya({io.MousePos.x - kok.x, io.MousePos.y - kok.y}); const pafta::Gorunum* oncu = p->gorunumBul(durum.surukleGorunum); const Vec2d eskiKonum = oncu ? oncu->konum : Vec2d{0, 0}; const Vec2d hedef{sf.x - durum.surukleKapma.x, sf.y - durum.surukleKapma.y}; p->gorunumTasi(durum.surukleGorunum, hedef); // Çoklu seçim: öncünün GERÇEK deltası (hiza kilidi kırpmış olabilir) kümedeki // diğerlerine uygulanır — grup ISO yerleşimini bozmadan birlikte kayar. if (oncu && durum.seciliKume.size() > 1) { const Vec2d delta{oncu->konum.x - eskiKonum.x, oncu->konum.y - eskiKonum.y}; for (u32 k : durum.seciliKume) { if (k == durum.surukleGorunum) continue; if (const pafta::Gorunum* g2 = p->gorunumBul(k)) p->gorunumTasi(k, {g2->konum.x + delta.x, g2->konum.y + delta.y}); } } // Hiza kilidi GÖRSEL kılavuzu (CL-11): hizalı görünüm sürüklenirken ebeveynle // arasındaki kilitli eksen ince kesikli çizgiyle gösterilir — "neden yalnız bu // yönde kayıyor" sorusu bakışta cevaplanır (DM: ipucu, diyalog değil). if (oncu && oncu->ust && oncu->hiza != pafta::Gorunum::Hiza::Bagimsiz) { if (const pafta::Gorunum* ebeveyn = p->gorunumBul(oncu->ust)) { std::vector kilavuz = {ekr(ebeveyn->konum), ekr(oncu->konum)}; kesikliCiz(ciz, kilavuz, SECILI, 1.0f, 6.0f); ciz->AddCircleFilled(ekr(ebeveyn->konum), 3.0f, SECILI); } } kareIste(2); } else { duzenKaydet("Görünüm Taşı"); durum.surukleGorunum = 0; } } } } // namespace fiko