#include "cam_strateji_semasi.hpp" #include #include #include #include "cam_takim_ekseni_penceresi.hpp" #include "fiko/cam/cam_islem.hpp" #include "fiko/cam/cam_secim.hpp" #include "fiko/cam/cam_sistemi.hpp" namespace fiko { namespace { using cam::CamGeometri; using cam::CamIslemTipCatia; using cam::CamOperasyon; using cam::CamSecimKip; using cam::CamSistemi; // Renk dili (cam_operasyon_penceresi.cpp/cam_paneli.cpp ile aynı). constexpr ImU32 REN_ZEMIN = IM_COL32(24, 26, 32, 255); constexpr ImU32 REN_CERCEVE = IM_COL32(70, 78, 92, 255); constexpr ImU32 REN_CEP = IM_COL32(20, 105, 47, 200); // dolu cep constexpr ImU32 REN_KONTUR_S = IM_COL32(255, 204, 51, 255); // seçili kontur (sarı) constexpr ImU32 REN_KONTUR_B = IM_COL32(51, 214, 255, 255); // bağlı kontur (cyan) constexpr ImU32 REN_EKSIK = IM_COL32(255, 96, 92, 255); // eksik (kırmızı) constexpr ImU32 REN_ADA = IM_COL32(255, 120, 90, 255); constexpr ImU32 REN_TOOLAXIS = IM_COL32(255, 120, 200, 255); constexpr ImU32 REN_LIMIT1 = IM_COL32(80, 220, 255, 255); constexpr ImU32 REN_LIMIT2 = IM_COL32(255, 120, 200, 255); constexpr ImU32 REN_LIMITUST = IM_COL32(90, 150, 255, 220); constexpr ImU32 REN_LIMITALT = IM_COL32(255, 130, 170, 220); constexpr ImU32 REN_ETIKET_Z = IM_COL32(16, 18, 24, 230); constexpr ImU32 REN_ETIKET_C = IM_COL32(60, 68, 82, 255); constexpr ImU32 REN_METIN = IM_COL32(220, 226, 234, 255); constexpr ImU32 REN_SOLUK = IM_COL32(140, 150, 165, 255); constexpr ImU32 REN_VURGU = IM_COL32(255, 255, 255, 90); // hover overlay // Şema kategori (web sekilKategori 173-182). enum class Kategori { Cep, Yuzey, Profil, Dairesel, Delik, Nokta }; Kategori kategori(CamIslemTipCatia t) { switch (t) { case CamIslemTipCatia::Pocketing: return Kategori::Cep; case CamIslemTipCatia::Facing: return Kategori::Yuzey; case CamIslemTipCatia::Contouring: case CamIslemTipCatia::CurveFollow: return Kategori::Profil; case CamIslemTipCatia::CircularMill: return Kategori::Dairesel; case CamIslemTipCatia::PointToPoint: return Kategori::Nokta; default: return cam::axialMi(t) ? Kategori::Delik : Kategori::Cep; } } // Takım ekseni kısa adı (web eksenAd 184). const char* eksenAd(const cam::TakimYonu& ty) { const Vec3d& y = ty.yon; if (std::abs(y.z) > 0.99) return "Z"; if (std::abs(y.x) > 0.99) return "X"; if (std::abs(y.y) > 0.99) return "Y"; return "Özel"; } // ---- İzometrik projeksiyon (spec 2.1) ------------------------------------- struct Izo { ImVec2 merkez; float ex_x, ex_y, ey_x, ey_y, ez_x, ez_y; // eksen ekran vektörleri (alokasyonsuz) ImVec2 pr(float x, float y, float z) const { return {merkez.x + x * ex_x + y * ey_x + z * ez_x, merkez.y + x * ex_y + y * ey_y + z * ez_y}; } }; // Rol enum'u (hit-test önceliği: küçük indeks daha öncelikli değil; el ile sıralanır). enum class Rol { Yok, Kontur, Ada, Nokta, ToolAxis, Limit1, Limit2, LimitUst, LimitAlt, OfsetUst, OfsetKontur, OfsetTaban, OfsetCheck }; // Nokta-daire içinde mi. bool daireIcinde(ImVec2 p, ImVec2 c, float r) { const float dx = p.x - c.x, dy = p.y - c.y; return dx * dx + dy * dy <= r * r; } bool dikdortgenIcinde(ImVec2 p, ImVec2 a, ImVec2 b) { const float x0 = a.x < b.x ? a.x : b.x, x1 = a.x < b.x ? b.x : a.x; const float y0 = a.y < b.y ? a.y : b.y, y1 = a.y < b.y ? b.y : a.y; return p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1; } // Op geometrisinden bir referans dörtgen (kontur bbox veya varsayılan) — şema // tüm varyantlarda ortak bir taban ölçek kullanır. Kontur yoksa L-profil çizilir. struct Sema { float yariGen = 20.0f; // yerel yarım genişlik (X) float yariDer = 14.0f; // yerel yarım derinlik (Y) float ustZ = 0.0f, altZ = -10.0f; bool konturVar = false; }; Sema semaOlc(const CamOperasyon& op) { Sema s; const CamGeometri& g = op.geometri; s.ustZ = 0.0f; s.altZ = (float)(g.altZ - g.ustZ); // yerel (üst = 0) if (s.altZ > -2.0f) s.altZ = -8.0f; // görsel minimum derinlik if (!g.konturlar.empty() && !g.konturlar[0].empty()) { s.konturVar = true; float minx = 1e9f, maxx = -1e9f, miny = 1e9f, maxy = -1e9f; for (const auto& kon : g.konturlar) for (const Vec2d& p : kon) { minx = std::min(minx, (float)p.x); maxx = std::max(maxx, (float)p.x); miny = std::min(miny, (float)p.y); maxy = std::max(maxy, (float)p.y); } const float gw = (maxx - minx) * 0.5f, gd = (maxy - miny) * 0.5f; if (gw > 0.5f) s.yariGen = gw; if (gd > 0.5f) s.yariDer = gd; } return s; } // Yerel kontur noktasını şema-yerel (merkezlenmiş) uzaya taşı. void konturMerkezle(const CamGeometri& g, Vec2d& off, float& olc) { float minx = 1e9f, maxx = -1e9f, miny = 1e9f, maxy = -1e9f; for (const auto& kon : g.konturlar) for (const Vec2d& p : kon) { minx = std::min(minx, (float)p.x); maxx = std::max(maxx, (float)p.x); miny = std::min(miny, (float)p.y); maxy = std::max(maxy, (float)p.y); } off.x = (minx + maxx) * 0.5; off.y = (miny + maxy) * 0.5; const float ext = std::max({maxx - minx, maxy - miny, 4.0f}); olc = 40.0f / ext; // şema hedef genişliği ~40 yerel birim } } // namespace void stratejiSemasiCiz(CamSistemi& cam, CamOperasyon& op, ImVec2 boyut, bool& degisti) { ImGui::PushID((int)(op.kimlik * 16 + 7)); const ImVec2 p0 = ImGui::GetCursorScreenPos(); if (boyut.x < 40.0f) boyut.x = ImGui::GetContentRegionAvail().x; if (boyut.x < 40.0f) { ImGui::PopID(); return; } ImDrawList* dl = ImGui::GetWindowDrawList(); ImGuiIO& io = ImGui::GetIO(); // Zemin. const ImVec2 p1 = {p0.x + boyut.x, p0.y + boyut.y}; dl->AddRectFilled(p0, p1, REN_ZEMIN, 5.0f); dl->AddRect(p0, p1, REN_CERCEVE, 5.0f); // Tek büyük görünmez buton — tüm hit-test manuel (spec 2.3 önerisi). ImGui::InvisibleButton("##sema", boyut); const bool hoverAlan = ImGui::IsItemHovered(); const ImVec2 fare = io.MousePos; const bool tik = ImGui::IsItemClicked(ImGuiMouseButton_Left); const bool ciftTik = hoverAlan && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left); const CamGeometri& g = op.geometri; const Kategori kat = kategori(op.tip); // İzometrik çerçeve. const float c = std::min(boyut.x, boyut.y) / 90.0f; // ölçek Izo iz; iz.merkez = {p0.x + boyut.x * 0.5f, p0.y + boyut.y * 0.56f}; iz.ex_x = c; iz.ex_y = -c * 0.5f; // +X sağ-aşağı iz.ey_x = -c; iz.ey_y = -c * 0.5f; // +Y sol-aşağı iz.ez_x = 0.0f; iz.ez_y = -c * 0.9f; // +Z yukarı const Sema sm = semaOlc(op); const float hx = std::min(sm.yariGen, 26.0f), hy = std::min(sm.yariDer, 20.0f); const float zU = 0.0f, zA = std::max(sm.altZ, -22.0f); // Aktif rol durum rengi: bağlı → cyan, seçili → sarı, eksik → kırmızı. const bool konturBagli = !g.konturlar.empty(); const bool konModuAktif = cam.secim().aktifMi(op.kimlik, CamSecimKip::Kontur) || cam.secim().aktifMi(op.kimlik, CamSecimKip::Kenar); const ImU32 konRenk = konModuAktif ? REN_KONTUR_S : konturBagli ? REN_KONTUR_B : REN_EKSIK; Rol hoverRol = Rol::Yok; auto testDaire = [&](Rol r, ImVec2 cc, float rr) { if (hoverAlan && hoverRol == Rol::Yok && daireIcinde(fare, cc, rr)) hoverRol = r; }; auto testDik = [&](Rol r, ImVec2 a, ImVec2 b) { if (hoverAlan && hoverRol == Rol::Yok && dikdortgenIcinde(fare, a, b)) hoverRol = r; }; // ---- Taban blok (izometrik prizma alt yüzü + kenarları) ---- const ImVec2 A = iz.pr(-hx, -hy, zA), B = iz.pr(hx, -hy, zA); const ImVec2 C = iz.pr(hx, hy, zA), D = iz.pr(-hx, hy, zA); const ImVec2 Au = iz.pr(-hx, -hy, zU), Bu = iz.pr(hx, -hy, zU); const ImVec2 Cu = iz.pr(hx, hy, zU), Du = iz.pr(-hx, hy, zU); // Gövde kutusu (yarı-saydam duvarlar, painter arkadan öne). dl->AddQuadFilled(A, B, Bu, Au, IM_COL32(40, 46, 56, 160)); // -Y duvar dl->AddQuadFilled(B, C, Cu, Bu, IM_COL32(52, 60, 72, 160)); // +X duvar dl->AddQuad(Au, Bu, Cu, Du, REN_CERCEVE, 1.2f); // üst çerçeve // ---- Varyanta göre üst şekil (spec 2.2) ---- if (kat == Kategori::Nokta || kat == Kategori::Delik) { // Delik/nokta işaretleri (üst yüz üzerinde). Kontur yoksa merkez tek işaret. int adet = (int)g.noktalar.size(); if (adet == 0) adet = 1; for (int i = 0; i < adet && i < 24; ++i) { float nx = 0, ny = 0; if (i < (int)g.noktalar.size()) { // Yerel nokta → merkezlenmiş şema uzayı (kaba ölçek). nx = (float)g.noktalar[(size_t)i].x * 0.4f; ny = (float)g.noktalar[(size_t)i].y * 0.4f; nx = std::clamp(nx, -hx, hx); ny = std::clamp(ny, -hy, hy); } else { nx = (i % 3 - 1) * hx * 0.5f; ny = ((i / 3) % 3 - 1) * hy * 0.5f; } const ImVec2 sp = iz.pr(nx, ny, zU); dl->AddCircle(sp, 5.0f, konturBagli ? REN_KONTUR_B : REN_EKSIK, 12, 1.6f); dl->AddLine({sp.x - 4, sp.y}, {sp.x + 4, sp.y}, REN_METIN, 1.2f); dl->AddLine({sp.x, sp.y - 4}, {sp.x, sp.y + 4}, REN_METIN, 1.2f); testDaire(Rol::Nokta, sp, 7.0f); } } else { // Kontur poligonu (varsa) yoksa L-profil placeholder. if (sm.konturVar) { Vec2d off; float olc; konturMerkezle(g, off, olc); for (size_t ci = 0; ci < g.konturlar.size(); ++ci) { const auto& kon = g.konturlar[ci]; if (kon.size() < 2) continue; const bool ada = false; // konturlar[*] hep dış; adalar ayrı çizilir (void)ada; // Üst kontur çizgisi (dolgu cep/facing için). const int n = (int)kon.size(); for (int i = 0; i < n; ++i) { const Vec2d& a = kon[(size_t)i]; const Vec2d& b = kon[(size_t)((i + 1) % n)]; const ImVec2 sa = iz.pr((float)((a.x - off.x) * olc), (float)((a.y - off.y) * olc), zU); const ImVec2 sb = iz.pr((float)((b.x - off.x) * olc), (float)((b.y - off.y) * olc), zU); dl->AddLine(sa, sb, konRenk, 2.4f); if (kat == Kategori::Profil) { // duvar şeridi: alta dikey çizgi const ImVec2 da = iz.pr((float)((a.x - off.x) * olc), (float)((a.y - off.y) * olc), zA); dl->AddLine(sa, da, IM_COL32(120, 150, 180, 140), 1.0f); } } } testDik(Rol::Kontur, Au, Cu); } else if (kat == Kategori::Dairesel) { const ImVec2 mc = iz.pr(0, 0, zU); dl->AddCircle(mc, hx * c * 0.9f + 4.0f, konRenk, 32, 2.4f); dl->AddCircleFilled(mc, 3.0f, konRenk); testDaire(Rol::Kontur, mc, hx * c); } else { // L-profil placeholder (henüz kontur seçilmemiş göstergesi). const float lx = hx * 0.9f, ly = hy * 0.9f; ImVec2 L[6] = {iz.pr(-lx, -ly, zU), iz.pr(lx, -ly, zU), iz.pr(lx, 0, zU), iz.pr(0, 0, zU), iz.pr(0, ly, zU), iz.pr(-lx, ly, zU)}; for (int i = 0; i < 6; ++i) dl->AddLine(L[i], L[(i + 1) % 6], REN_EKSIK, 2.0f); testDik(Rol::Kontur, Au, Cu); } // Cep dolgusu (yalnız cep varyantı, kontur varsa). if (kat == Kategori::Cep && sm.konturVar) { // Basit iç dolgu: üst yüz dörtgenini koyu-yeşil (kaba görsel). dl->AddQuadFilled(Au, Bu, Cu, Du, REN_CEP); } // Ada işareti (cep, adalar varsa). if (kat == Kategori::Cep && !g.adalar.empty()) { const ImVec2 amc = iz.pr(0, 0, zU + (zU - zA) * 0.0f); dl->AddCircleFilled(amc, 5.0f, REN_ADA); dl->AddLine({amc.x, amc.y}, {amc.x, amc.y - 10}, REN_ADA, 1.6f); testDaire(Rol::Ada, amc, 7.0f); } } // ---- Limit küreleri (profil varyantı: Limit1/Limit2) ---- if (kat == Kategori::Profil) { const ImVec2 l1 = iz.pr(-hx, -hy, zU); const ImVec2 l2 = iz.pr(hx, hy, zU); dl->AddCircleFilled(l1, 5.0f, REN_LIMIT1); dl->AddCircleFilled(l2, 5.0f, REN_LIMIT2); testDaire(Rol::Limit1, l1, 7.0f); testDaire(Rol::Limit2, l2, 7.0f); } // ---- Üst/Alt limit düzlem çerçeveleri (freze/cep) ---- const bool axial = cam::axialMi(op.tip); if (!axial) { dl->AddQuad(Au, Bu, Cu, Du, REN_LIMITUST, 1.4f); dl->AddQuad(A, B, C, D, REN_LIMITALT, 1.4f); testDik(Rol::LimitUst, Au, Cu); // Alt çerçeve hit alanı üst ile çakışmasın diye alt yarıya sınırla. if (hoverAlan && hoverRol == Rol::Yok && dikdortgenIcinde(fare, A, C) && fare.y > (Au.y + A.y) * 0.5f) hoverRol = Rol::LimitAlt; } // ---- Takım ekseni oku (spec 2.5) ---- { const ImVec2 taBas = iz.pr(0, 0, zU); const Vec3d& yy = op.takimYonu.yon; const ImVec2 taUc = iz.pr((float)yy.x * hx * 0.3f, (float)yy.y * hy * 0.3f, zU + (float)yy.z * 18.0f + 6.0f); dl->AddLine(taBas, taUc, REN_TOOLAXIS, 2.6f); // Koni uç (küçük üçgen, ok yönünde). const float ang = std::atan2(taUc.y - taBas.y, taUc.x - taBas.x); const float ka = 5.0f; dl->AddTriangleFilled(taUc, {taUc.x - ka * std::cos(ang - 0.5f), taUc.y - ka * std::sin(ang - 0.5f)}, {taUc.x - ka * std::cos(ang + 0.5f), taUc.y - ka * std::sin(ang + 0.5f)}, REN_TOOLAXIS); char te[24]; std::snprintf(te, sizeof(te), "⊥%s", eksenAd(op.takimYonu)); dl->AddText({taUc.x + 4, taUc.y - 6}, REN_TOOLAXIS, te); testDik(Rol::ToolAxis, {taBas.x - 8, taUc.y - 8}, {taUc.x + 8, taBas.y + 4}); } // ---- Offset etiketleri (spec 2.4) — çift-tık düzenle ---- struct EtiketBilgi { Rol rol; const char* ad; float deger; ImVec2 poz; }; EtiketBilgi etiketler[4] = { {Rol::OfsetUst, "Üst", (float)g.ofsetUst, iz.pr(0, -hy - 8, zU)}, {Rol::OfsetKontur, "Kontür", (float)g.ofsetKontur, iz.pr(hx + 8, 0, (zU + zA) * 0.5f)}, {Rol::OfsetTaban, "Taban", (float)g.ofsetTaban, iz.pr(0, hy + 8, zA)}, {Rol::OfsetCheck, "Check", (float)g.ofsetCheck, iz.pr(-hx - 8, 0, zU)}, }; Rol ofsetHover = Rol::Yok; for (const EtiketBilgi& e : etiketler) { char buf[48]; std::snprintf(buf, sizeof(buf), "%s %.2f", e.ad, e.deger); const ImVec2 ts = ImGui::CalcTextSize(buf); const ImVec2 a = {e.poz.x - ts.x * 0.5f - 4, e.poz.y - ts.y * 0.5f - 2}; const ImVec2 b = {e.poz.x + ts.x * 0.5f + 4, e.poz.y + ts.y * 0.5f + 2}; // Merkeze soluk bağlantı. dl->AddLine(iz.merkez, e.poz, IM_COL32(90, 100, 115, 90), 1.0f); const bool eh = hoverAlan && dikdortgenIcinde(fare, a, b); dl->AddRectFilled(a, b, eh ? REN_ETIKET_C : REN_ETIKET_Z, 3.0f); dl->AddRect(a, b, REN_CERCEVE, 3.0f); dl->AddText({a.x + 4, a.y + 2}, REN_METIN, buf); if (eh) ofsetHover = e.rol; } // ---- Hover vurgu overlay (rol daire/dikdörtgen) ---- if (hoverRol != Rol::Yok || ofsetHover != Rol::Yok) { // İmleç yakınına yumuşak halka (web vurgula 263-273). dl->AddCircle(fare, 10.0f, REN_VURGU, 16, 2.0f); } // ---- Tıklama dispatch (spec 2.3 tablo) ---- auto gc = [] {}; if (tik && hoverRol != Rol::Yok) { switch (hoverRol) { case Rol::Kontur: cam.secim().baslat(op, CamSecimKip::Kontur, gc); break; case Rol::Ada: cam.secim().baslat(op, CamSecimKip::Ada, gc); break; case Rol::Nokta: cam.secim().baslat(op, CamSecimKip::Nokta, gc); break; case Rol::ToolAxis: camTakimEkseniPencereAc(op.kimlik); break; // TRIAD penceresini aç case Rol::Limit1: cam.secim().duzlemBaslat(op, CamSecimKip::DuzlemUst, [&op](f64 z, u32 f) { op.geometri.baslangic = {op.geometri.baslangic.x, z}; (void)f; }, gc); break; case Rol::Limit2: cam.secim().duzlemBaslat(op, CamSecimKip::DuzlemAlt, [&op](f64 z, u32 f) { op.geometri.bitis = {op.geometri.bitis.x, z}; (void)f; }, gc); break; case Rol::LimitUst: { CamGeometri& gg = op.geometri; cam.secim().duzlemBaslat(op, CamSecimKip::DuzlemUst, [&gg](f64 z, u32 f) { gg.ustZ = z; gg.ustZRef = f; }, gc); break; } case Rol::LimitAlt: { CamGeometri& gg = op.geometri; cam.secim().duzlemBaslat(op, CamSecimKip::DuzlemAlt, [&gg](f64 z, u32 f) { gg.altZ = z; gg.altZRef = f; }, gc); break; } default: break; } } // ---- Offset çift-tık → inline editör ---- static u32 s_duzOp = 0; static Rol s_duzRol = Rol::Yok; if (ciftTik && ofsetHover != Rol::Yok) { s_duzOp = op.kimlik; s_duzRol = ofsetHover; } ImGui::SetCursorScreenPos({p0.x, p1.y + 2}); // Aşağıdaki blok KOŞULLU: düzenleme aktif değilse hiçbir öğe gönderilmez ve ImGui // "SetCursorPos ile sınır büyütme, öğe submit et" uyarısı basar (agac_gorunum'daki // ile aynı desen). Sıfır-boyut Dummy sınırı geçerli kılar, yerleşimi değiştirmez. ImGui::Dummy(ImVec2(0.0f, 0.0f)); if (s_duzOp == op.kimlik && s_duzRol != Rol::Yok) { f64* hedef = nullptr; const char* ad = ""; switch (s_duzRol) { case Rol::OfsetUst: hedef = &op.geometri.ofsetUst; ad = "Üst Ofset"; break; case Rol::OfsetKontur: hedef = &op.geometri.ofsetKontur; ad = "Kontür Ofset"; break; case Rol::OfsetTaban: hedef = &op.geometri.ofsetTaban; ad = "Taban Ofset"; break; case Rol::OfsetCheck: hedef = &op.geometri.ofsetCheck; ad = "Check Ofset"; break; default: break; } if (hedef) { ImGui::SetNextItemWidth(boyut.x - 70.0f); char lbl[48]; std::snprintf(lbl, sizeof(lbl), "%s##ofdz", ad); if (ImGui::InputScalar(lbl, ImGuiDataType_Double, hedef, nullptr, nullptr, "%.3f", ImGuiInputTextFlags_EnterReturnsTrue)) { degisti = true; s_duzRol = Rol::Yok; s_duzOp = 0; } ImGui::SameLine(); if (ImGui::SmallButton("✓")) { degisti = true; s_duzRol = Rol::Yok; s_duzOp = 0; } } else { s_duzRol = Rol::Yok; s_duzOp = 0; } } else { ImGui::TextColored(ImVec4{0.55f, 0.60f, 0.68f, 1.0f}, "Şemaya tıkla: rol seç · offset çift-tık: düzenle"); } ImGui::PopID(); } } // namespace fiko