#include "cam_takim_penceresi.hpp" #include #include #include #include #include #include #include "fiko/cam/cam_sistemi.hpp" #include "fiko/cam/takim.hpp" #include "fiko/cekirdek/cekirdek.hpp" #include "fiko/sahne/sahne.hpp" #include "fiko/ui/bilesen.hpp" #include "fiko/ui/form.hpp" #include "fiko/ui/pencere_cerceve.hpp" #include "fiko/ui/tema.hpp" #include "imgui.h" #include "pencere_util.hpp" namespace fiko { namespace { using cam::CamSistemi; using cam::Takim; using cam::TakimTip; // Profil/kenar renkleri (spec §4.2 neon-cyan tema). constexpr ImU32 GOVDE_DOLGU = IM_COL32(176, 192, 208, 255); // parlak celik constexpr ImU32 GOVDE_KENAR = IM_COL32(56, 189, 248, 255); // cyan #38bdf8 constexpr ImU32 SAP_DOLGU = IM_COL32(174, 185, 198, 255); // gri #AEB9C6 constexpr ImU32 EKSEN_RENK = IM_COL32(96, 130, 170, 120); // soluk mavi kesikli // Acik pencere + pin durumu (UI-only, kalici degil). cam_operasyon_penceresi.cpp // g_opAcik/g_opPin kalibi. Anahtar = takim numara. std::unordered_map g_tkAcik; std::unordered_map g_tkPin; // f64 icin DragScalar sarimi (cam_paneli.cpp ile ayni). bool surukleF64(const char* etiket, f64& deger, f32 hiz = 0.1f, const char* fmt = "%.3f") { return ImGui::DragScalar(etiket, ImGuiDataType_Double, °er, hiz, nullptr, nullptr, fmt); } // Tip dropdown sirasi (spec §1: web dialog tipler[] ile ayni). Native enum // sirasindan bagimsiz — UI sunumu bu diziyle sabit tutulur. constexpr TakimTip TIP_SIRA[] = { TakimTip::ParmakFreze, // faceMill (native ayri degil; duz-dip cizilir) TakimTip::ParmakFreze, // endMill TakimTip::KureselFreze, // ballMill TakimTip::TorusFreze, // bullMill TakimTip::KonikFreze, // conicalMill TakimTip::ChamferMill, // chamferMill TakimTip::TSlotMill, // tslotMill TakimTip::ThreadMill, // threadMill TakimTip::CenterDrill, // centerDrill TakimTip::PuntaMatkap, // spotDrill TakimTip::Matkap, // drill TakimTip::MultiDrill, // multiDrill TakimTip::Havsa, // countersink TakimTip::CounterBore, // counterbore TakimTip::Rayba, // reamer TakimTip::BoringBar, // boringBar TakimTip::Kilavuz, // tap }; // ============================ KOSULLU GORUNURLUK (spec §3) =================== bool koseVar(TakimTip t) { return t == TakimTip::KureselFreze || t == TakimTip::TorusFreze || t == TakimTip::TSlotMill; } bool ucVar(TakimTip t) { return t == TakimTip::Matkap || t == TakimTip::PuntaMatkap || t == TakimTip::CenterDrill || t == TakimTip::Havsa || t == TakimTip::ChamferMill; } bool hatveVar(TakimTip t) { return t == TakimTip::Kilavuz || t == TakimTip::ThreadMill; } bool kademeliVar(TakimTip t) { return t == TakimTip::MultiDrill; } bool girisVar(TakimTip t) { return t == TakimTip::KonikFreze || t == TakimTip::Havsa || t == TakimTip::ChamferMill || t == TakimTip::CounterBore || t == TakimTip::Rayba; } bool aci2Var(TakimTip t) { return t == TakimTip::CenterDrill || t == TakimTip::MultiDrill; } bool kademeBoyVar(TakimTip t) { return t == TakimTip::MultiDrill || t == TakimTip::CounterBore; } bool tslotVar(TakimTip t) { return t == TakimTip::TSlotMill; } // ============================ RADYAL PROFIL (spec §4.1) ====================== // Aci → radyan, 0-bolme korumasi icin 1..179° clamp. f64 aciRad(f64 a, f64 def) { f64 v = (a > 0.0) ? a : def; v = std::clamp(v, 1.0, 179.0); return v * std::numbers::pi / 180.0; } // Yay noktalari (cx,cy merkez, r yaricap, a0..a1 radyan, n+1 nokta). void yay(std::vector& p, f64 cx, f64 cy, f64 r, f64 a0, f64 a1, int n = 10) { for (int i = 0; i <= n; ++i) { const f64 a = a0 + (a1 - a0) * (f64)i / (f64)n; p.push_back(ImVec2((f32)(cx + r * std::cos(a)), (f32)(cy + r * std::sin(a)))); } } // Radyal profil: x=yaricap(mm), y=uctan yukseklik(mm). UI + (ileride) 3D revolve // PAYLASIR (spec §7.4 tek fonksiyon ilkesi). Uc y=0, kesici ustu y=Lc. std::vector takimProfilNoktalari(const Takim& t) { std::vector p; const f64 R = t.cap * 0.5; const f64 Lc = std::max(1.0, t.boy); const f64 D90 = std::numbers::pi * 0.5; // 90° rad switch (t.tip) { case TakimTip::KureselFreze: // ballMill: yarim kure uc (merkez y=R) p.push_back({0, 0}); yay(p, 0, R, R, -D90, 0); p.push_back({(f32)R, (f32)Lc}); break; case TakimTip::TorusFreze: { // bullMill: kose radyus const f64 k = std::min(R, t.koseYaricap); p.push_back({0, 0}); p.push_back({(f32)(R - k), 0}); yay(p, R - k, k, k, -D90, 0); p.push_back({(f32)R, (f32)Lc}); break; } case TakimTip::Matkap: case TakimTip::PuntaMatkap: { // drill/spotDrill: konik matkap ucu const f64 h = R / std::tan(aciRad(t.ucAci, 118) * 0.5); p.push_back({0, 0}); p.push_back({(f32)R, (f32)h}); p.push_back({(f32)R, (f32)Lc}); break; } case TakimTip::CenterDrill: { // pilot + taper const f64 rp = R * 0.5; const f64 hTip = rp / std::tan(aciRad(t.ucAci, 90) * 0.5); const f64 hTap = (R - rp) / std::tan(aciRad(t.ucAci2, 60) * 0.5); p.push_back({0, 0}); p.push_back({(f32)rp, (f32)hTip}); p.push_back({(f32)R, (f32)(hTip + hTap)}); p.push_back({(f32)R, (f32)Lc}); break; } case TakimTip::KonikFreze: { // conicalMill: konik (ust R, alt rb) const f64 rb = (t.girisCap > 0.0) ? t.girisCap * 0.5 : R * 0.55; p.push_back({0, 0}); p.push_back({(f32)rb, 0}); p.push_back({(f32)R, (f32)Lc}); break; } case TakimTip::Havsa: case TakimTip::ChamferMill: { // havsa/pah ters koni const f64 h = R / std::tan(aciRad(t.ucAci, 90) * 0.5); const f64 rd = (t.girisCap > 0.0) ? t.girisCap * 0.5 : R * 0.25; p.push_back({0, 0}); p.push_back({(f32)rd, 0}); p.push_back({(f32)rd, (f32)(h * 0.15)}); p.push_back({(f32)R, (f32)h}); p.push_back({(f32)R, (f32)Lc}); break; } case TakimTip::MultiDrill: { // kademeli + uc koni const f64 r1 = R; const f64 r2 = ((t.cap2 > 0.0) ? t.cap2 : t.cap) * 0.5; const f64 r3 = ((t.cap3 > 0.0) ? t.cap3 : (t.cap2 > 0.0 ? t.cap2 : t.cap)) * 0.5; const f64 hTip = r1 / std::tan(aciRad(t.ucAci, 118) * 0.5); const f64 l1 = (t.kademeBoy1 > 0.0) ? t.kademeBoy1 : t.boy * 0.3; const f64 l2 = (t.kademeBoy2 > 0.0) ? t.kademeBoy2 : t.boy * 0.3; const f64 yk1 = hTip, yk2 = yk1 + l1, yk3 = yk2 + l2; p.push_back({0, 0}); p.push_back({(f32)r1, (f32)yk1}); p.push_back({(f32)r1, (f32)yk2}); p.push_back({(f32)r2, (f32)yk2}); p.push_back({(f32)r2, (f32)yk3}); p.push_back({(f32)r3, (f32)yk3}); p.push_back({(f32)r3, (f32)Lc}); break; } case TakimTip::CounterBore: { // 2 kademe duz const f64 rd = (t.girisCap > 0.0) ? t.girisCap * 0.5 : R * 0.5; const f64 l1 = (t.kademeBoy1 > 0.0) ? t.kademeBoy1 : t.boy * 0.4; p.push_back({0, 0}); p.push_back({(f32)rd, 0}); p.push_back({(f32)rd, (f32)l1}); p.push_back({(f32)R, (f32)l1}); p.push_back({(f32)R, (f32)Lc}); break; } case TakimTip::TSlotMill: { // T disk const f64 neck = R * 0.4; const f64 diskH = Lc * 0.4; p.push_back({0, 0}); p.push_back({(f32)R, 0}); p.push_back({(f32)R, (f32)diskH}); p.push_back({(f32)neck, (f32)diskH}); p.push_back({(f32)neck, (f32)Lc}); break; } case TakimTip::BoringBar: { // asimetrik bara const f64 rn = R * 0.4; p.push_back({0, 0}); p.push_back({(f32)R, (f32)(R * 0.4)}); p.push_back({(f32)R, (f32)(R * 0.6)}); p.push_back({(f32)rn, (f32)(R * 0.6)}); p.push_back({(f32)rn, (f32)Lc}); break; } case TakimTip::Rayba: case TakimTip::Kilavuz: // reamer/tap: hafif konik/kut uc p.push_back({0, 0}); p.push_back({(f32)R, (f32)(R * 0.3)}); p.push_back({(f32)R, (f32)Lc}); break; default: // faceMill/endMill/threadMill: duz dip p.push_back({0, 0}); p.push_back({(f32)R, 0}); p.push_back({(f32)R, (f32)Lc}); break; } return p; } // 2B yan profil ciz (spec §4.2): radyal profili aynala + sap + eksen. void takimProfilCiz(ImDrawList* dl, const ImVec2& p0, const ImVec2& sz, const Takim& t) { const std::vector rad = takimProfilNoktalari(t); if (rad.empty()) return; // Olcek: L = toplam boy (yoksa boy*2.5), maxCap = en genis yaricap*2. const f64 L = std::max(1.0, (t.toplamBoy > 0.0) ? t.toplamBoy : t.boy * 2.5); f64 maxR = 0.25; for (const ImVec2& v : rad) maxR = std::max(maxR, (f64)v.x); maxR = std::max({maxR, t.cap * 0.5, t.sapCap * 0.5}); const f64 maxCap = maxR * 2.0; const f32 kenarPay = 14.0f; const f64 sc = std::min((sz.y - 2 * kenarPay) / L, (sz.x - 2 * kenarPay) / maxCap); const f32 cx = p0.x + sz.x * 0.5f; const f32 taban = p0.y + sz.y - kenarPay; // uc (y=0) ekranin altinda auto ekran = [&](f64 r, f64 y) -> ImVec2 { return ImVec2(cx + (f32)(r * sc), taban - (f32)(y * sc)); }; // Eksen cizgisi (kesikli soluk mavi). { const f32 ust = taban - (f32)(L * sc); for (f32 y = ust; y < taban; y += 8.0f) dl->AddLine(ImVec2(cx, y), ImVec2(cx, std::min(y + 4.0f, taban)), EKSEN_RENK, 1.0f); } // Sap (shank): kesicinin ustunde dikdortgen. Kesici ustu y=Lc, sap y=Lc..L. const f64 Lc = std::max(1.0, t.boy); const f64 sapR = (t.sapCap > 0.0) ? t.sapCap * 0.5 : t.cap * 0.31; // capPx*0.62 → yaricap*0.31... web sapW=cap*0.62 (cap!) → yaricap 0.31 if (L > Lc) { const ImVec2 a = ekran(-sapR, Lc); const ImVec2 b = ekran(sapR, L); dl->AddRectFilled(ImVec2(a.x, b.y), ImVec2(b.x, a.y), SAP_DOLGU, 2.0f); // Sap-kesici omuz gecisi (kesici ust yaricapi ≠ sapR ise trapez). const f64 ustR = rad.back().x; if (std::abs(ustR - sapR) > 0.05) { const ImVec2 s0 = ekran(-ustR, Lc), s1 = ekran(ustR, Lc); const ImVec2 s2 = ekran(sapR, Lc), s3 = ekran(-sapR, Lc); dl->AddQuadFilled(s0, s1, s2, s3, SAP_DOLGU); } } // Kesici govde: radyal profili +x/−x aynala → kapali konkav polygon. std::vector poly; poly.reserve(rad.size() * 2); for (const ImVec2& v : rad) poly.push_back(ekran(v.x, v.y)); // sag kenar (uc→ust) for (auto it = rad.rbegin(); it != rad.rend(); ++it) poly.push_back(ekran(-it->x, it->y)); // sol kenar (ust→uc) dl->AddConcavePolyFilled(poly.data(), (int)poly.size(), GOVDE_DOLGU); dl->AddPolyline(poly.data(), (int)poly.size(), GOVDE_KENAR, ImDrawFlags_Closed, 1.6f); } // ============================ FORM (spec §6.2) ============================== // Combo secilenden native TakimTip'e (TIP_SIRA indeksi). ballMill'e gecerken // kose otomatik dolar (spec §6.2 setTip). void tipCombo(Takim& t, bool& degisti) { // Aktif indeks: mevcut tip'e karsilik ilk TIP_SIRA konumu. int aktif = 1; // endMill varsayilan for (int i = 0; i < (int)IM_ARRAYSIZE(TIP_SIRA); ++i) if (TIP_SIRA[i] == t.tip) { aktif = i; break; } static const char* ADLAR[] = { "Tarama Kafası", "Parmak Freze", "Küresel Freze", "Toroidal Freze", "Konik Freze", "Pah Freze", "T-Kanal Freze", "Diş Freze", "Punta Matkap", "Spot Matkap", "Matkap", "Çok-Çaplı Matkap", "Havşa Matkap", "Havşa Düz", "Rayba", "Bara Çubuğu", "Kılavuz"}; ui::alan("Tip"); if (ImGui::Combo("##Tip", &aktif, ADLAR, IM_ARRAYSIZE(ADLAR))) { t.tip = TIP_SIRA[aktif]; if (t.tip == TakimTip::KureselFreze && t.koseYaricap <= 0.0) t.koseYaricap = t.cap * 0.5; degisti = true; } } void parametreFormu(Takim& t, bool& degisti) { { char buf[96]; std::snprintf(buf, sizeof(buf), "%s", t.ad.c_str()); ui::alan("Ad"); if (ImGui::InputText("##Ad", buf, sizeof(buf))) { t.ad = buf; degisti = true; } } tipCombo(t, degisti); ImGui::SeparatorText("Ölçüler"); ui::alan("Çap (D)"); degisti |= surukleF64("##Çap", t.cap, 0.05f, "%.2f"); ui::alan("Kesici Boyu (Lc)"); degisti |= surukleF64("##Kesici Boyu", t.boy, 0.5f, "%.1f"); ui::alan("Toplam Boy (L)"); degisti |= surukleF64("##Toplam Boy", t.toplamBoy, 0.5f, "%.1f"); ui::alan("Sap Çapı (Ds)"); degisti |= surukleF64("##Sap Çapı", t.sapCap, 0.05f, "%.2f"); const TakimTip tp = t.tip; if (kademeliVar(tp)) { ui::alan("2. Kademe Çap (Dc)"); degisti |= surukleF64("##2. Kademe Çap", t.cap2, 0.05f, "%.2f"); ui::alan("3. Kademe Çap (Dc2)"); degisti |= surukleF64("##3. Kademe Çap", t.cap3, 0.05f, "%.2f"); } if (girisVar(tp)) { ui::alan("Giriş/Alt Çap (d)"); degisti |= surukleF64("##Giriş/Alt Çap", t.girisCap, 0.05f, "%.2f"); } if (koseVar(tp)) { ui::alan(tslotVar(tp) ? "Alt Kesim R (Rc)" : "Köşe Yarıçap (Rc)"); degisti |= surukleF64("##Köşe R", t.koseYaricap, 0.05f, "%.2f"); } if (tslotVar(tp)) { ui::alan("Üst Kesim R (Rc2)"); degisti |= surukleF64("##Üst Kesim R", t.kose2, 0.05f, "%.2f"); } if (kademeBoyVar(tp)) { ui::alan("1. Kademe Boy (l1)"); degisti |= surukleF64("##1. Kademe Boy", t.kademeBoy1, 0.5f, "%.1f"); } if (kademeBoyVar(tp) && kademeliVar(tp)) { ui::alan("2. Kademe Boy (l2)"); degisti |= surukleF64("##2. Kademe Boy", t.kademeBoy2, 0.5f, "%.1f"); } if (ucVar(tp) || kademeliVar(tp)) { ui::alan(aci2Var(tp) ? "1. Açı (a1)" : "Uç Açısı"); degisti |= surukleF64("##Uç Açısı", t.ucAci, 1.0f, "%.0f"); } if (aci2Var(tp)) { ui::alan("2. Açı (a2)"); degisti |= surukleF64("##2. Açı", t.ucAci2, 1.0f, "%.0f"); } if (kademeliVar(tp)) { ui::alan("Taper Açısı (a3)"); degisti |= surukleF64("##Taper Açısı", t.ucAci3, 1.0f, "%.0f"); } if (hatveVar(tp)) { ui::alan("Hatve (P)"); degisti |= surukleF64("##Hatve", t.hatve, 0.05f, "%.2f"); } ImGui::SeparatorText("Kesim"); ui::alan("Ağız Sayısı (Nf)"); if (ImGui::DragInt("##Ağız Sayısı", &t.agizSayisi, 0.1f, 1, 12)) degisti = true; ui::alan("Takım No (T)"); if (ImGui::DragInt("##Takım No", &t.numara, 0.1f, 0, 999)) degisti = true; } // ============================ KUTUPHANE (spec §5) =========================== // Takim numara kac operasyonda kullaniliyor (CamIslem.takimNo + CamOperasyon.takimId). int takimKullanimSayisi(CamSistemi& cam, int numara) { int say = 0; for (const cam::CamIslem& op : cam.hepsi()) if (op.takimNo == numara) ++say; for (const cam::CamOperasyon& op : cam.operasyonlar()) if (op.takimId == numara) ++say; return say; } int enBuyukNumara(CamSistemi& cam) { int mx = 0; for (const Takim& t : cam.takimlar()) mx = std::max(mx, t.numara); return mx; } // ============================ PENCERE ====================================== void takimPenceresiCiz(Cekirdek& cekirdek, Sahne& sahne, CamSistemi& cam, Takim& t) { (void)cekirdek; (void)sahne; using namespace fiko::ui; const ImVec4 HATA_RENK = ui::tema().hata; bool& ustte = g_tkPin[t.numara]; char baslik[176]; std::snprintf(baslik, sizeof(baslik), "🔧 Takım Tanımı — %s###tk_%d", t.ad.c_str(), t.numara); bool acik = true; bool silindi = false; { // Yan-yana profil/form yerleşimi sabit yükseklik ister (çocuk -altBar dolgusu). Pencere p{baslik, &acik, {.boyut = {560, 420}, .pinDurum = &ustte}}; if (p) { bool degisti = false; const f32 altBar = ImGui::GetFrameHeightWithSpacing() + 6.0f; const f32 solEn = ImGui::GetContentRegionAvail().x * 0.44f; // ---- Sol: canli 2B yan profil ---- ImGui::BeginChild("profil", {solEn, -altBar}, ImGuiChildFlags_Borders); { const ImVec2 p0 = ImGui::GetCursorScreenPos(); const ImVec2 sz = ImGui::GetContentRegionAvail(); ImDrawList* dl = ImGui::GetWindowDrawList(); dl->AddRectFilled(p0, ImVec2(p0.x + sz.x, p0.y + sz.y), IM_COL32(18, 24, 34, 255), 4.0f); takimProfilCiz(dl, p0, sz, t); // T-no rozeti (sol ust, sari). char rozet[16]; std::snprintf(rozet, sizeof(rozet), "T%d", t.numara); const ImVec2 rsz = ImGui::CalcTextSize(rozet); dl->AddRectFilled(ImVec2(p0.x + 6, p0.y + 6), ImVec2(p0.x + 14 + rsz.x, p0.y + 10 + rsz.y), IM_COL32(250, 204, 21, 235), 3.0f); dl->AddText(ImVec2(p0.x + 10, p0.y + 8), IM_COL32(20, 20, 20, 255), rozet); } ImGui::EndChild(); ImGui::SameLine(); // ---- Sag: kosullu parametre formu ---- ImGui::BeginChild("form", {0, -altBar}); parametreFormu(t, degisti); ImGui::EndChild(); // ---- Alt eylem cubugu ---- ImGui::Separator(); if (ImGui::Button("✔ Tamam", {-84, 0})) acik = false; ImGui::SameLine(); if (ikonButon("⧉", "Takımı kopyala")) { Takim kopya = t; kopya.numara = enBuyukNumara(cam) + 1; kopya.ad += " (Kopya)"; cam.takimlar().push_back(kopya); camTakimPencereAc(kopya.numara); } ImGui::SameLine(); const int kullanim = takimKullanimSayisi(cam, t.numara); ImGui::BeginDisabled(kullanim > 0); if (ikonButon("🗑", "Takımı sil")) { auto& lst = cam.takimlar(); auto it = std::find_if(lst.begin(), lst.end(), [&](const Takim& x) { return x.numara == t.numara; }); if (it != lst.end()) { const int kapatNo = t.numara; // t erase sonrasi gecersiz lst.erase(it); g_tkAcik[kapatNo] = false; silindi = true; } } ImGui::EndDisabled(); if (!silindi && kullanim > 0) { ImGui::SameLine(); ImGui::TextColored(HATA_RENK, "%d op kullanıyor", kullanim); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Takım %d operasyonda kullanılıyor — silinemez", t.numara); } (void)degisti; // immediate-mode: profil her kare yeniden cizilir } } if (silindi) return; if (!acik) g_tkAcik[t.numara] = false; } } // namespace void camTakimPencereAc(int numara) { g_tkAcik[numara] = true; } void camTakimPencereleriCiz(Cekirdek& cekirdek, Sahne& sahne, cam::CamSistemi& cam) { for (auto it = g_tkAcik.begin(); it != g_tkAcik.end();) { if (!it->second) { it = g_tkAcik.erase(it); continue; } Takim* t = cam.takimBul(it->first); if (!t) { it = g_tkAcik.erase(it); continue; } // takim silindi takimPenceresiCiz(cekirdek, sahne, cam, *t); ++it; } } } // namespace fiko