#include "fiko/ui/bilesen_cad.hpp" #include #include #include #include "fiko/ui/animasyon.hpp" #include "fiko/ui/bilesen.hpp" #include "fiko/ui/tema.hpp" #include "imgui_internal.h" namespace fiko::ui { namespace { constexpr float PI = 3.14159265f; const ImVec4 EKSEN[3] = { {0.95f, 0.30f, 0.30f, 1}, {0.30f, 0.85f, 0.35f, 1}, {0.30f, 0.55f, 1.0f, 1}}; void metniOrtala(ImDrawList* dl, ImVec2 merkez, float boyut, ImU32 renk, const char* metin) { ImFont* f = ImGui::GetFont(); const ImVec2 mb = f->CalcTextSizeA(boyut, FLT_MAX, 0.0f, metin); dl->AddText(f, boyut, {merkez.x - mb.x * 0.5f, merkez.y - mb.y * 0.5f}, renk, metin); } } // namespace bool aciKadran(const char* etiket, float* derece, float cap) { const Tema& t = tema(); ImGui::PushID(etiket); const ImVec2 p = ImGui::GetCursorScreenPos(); const float r = cap * 0.5f; const ImVec2 m{p.x + r, p.y + r}; ImGui::InvisibleButton("##kadran", {cap, cap}); const bool aktif = ImGui::IsItemActive(); const bool hover = ImGui::IsItemHovered(); const ImGuiID id = ImGui::GetItemID(); const float h = animf(id, (hover || aktif) ? 1.0f : 0.0f, 12.0f); bool degisti = false; if (aktif) { const ImVec2 fare = ImGui::GetMousePos(); float a = std::atan2(fare.y - m.y, fare.x - m.x) * 180.0f / PI + 90.0f; while (a < 0) a += 360.0f; while (a >= 360.0f) a -= 360.0f; if (ImGui::GetIO().KeyCtrl) a = std::round(a / 5.0f) * 5.0f; if (a != *derece) { *derece = a; degisti = true; } } ImDrawList* dl = ImGui::GetWindowDrawList(); // zemin + dış halka dl->AddCircleFilled(m, r, u32(t.panel, 0.9f)); if (h > 0.02f) for (int i = 1; i <= 3; ++i) dl->AddCircle(m, r + (float)i * 1.8f, u32(t.vurgu, 0.15f * h * t.glowSiddet / i), 0, 2.0f); dl->AddCircle(m, r, u32(t.vurgu, 0.30f + 0.45f * h), 0, 1.5f); // tik işaretleri (15°'de kısa, 90°'de uzun) for (int a = 0; a < 360; a += 15) { const float rad = ((float)a - 90.0f) * PI / 180.0f; const float ic = a % 90 == 0 ? r - 11.0f : r - 6.0f; const ImU32 renk = u32(a % 90 == 0 ? t.metin : t.metinSoluk, 0.55f); dl->AddLine({m.x + std::cos(rad) * ic, m.y + std::sin(rad) * ic}, {m.x + std::cos(rad) * (r - 2.5f), m.y + std::sin(rad) * (r - 2.5f)}, renk, a % 90 == 0 ? 1.8f : 1.0f); } // yay: 0° → değer (neon) const float hedefAci = animf(id + 1, *derece, 18.0f); const float aBas = -PI * 0.5f, aSon = aBas + hedefAci * PI / 180.0f; dl->PathArcTo(m, r - 4.5f, aBas, aSon, 48); dl->PathStroke(u32(t.vurgu, 0.85f), 0, 3.0f); dl->PathArcTo(m, r - 4.5f, aBas, aSon, 48); dl->PathStroke(u32(t.vurgu, 0.22f * t.glowSiddet), 0, 7.0f); // iğne + uç topu const ImVec2 uc{m.x + std::cos(aSon) * (r - 8.0f), m.y + std::sin(aSon) * (r - 8.0f)}; dl->AddLine(m, uc, u32(t.metin, 0.8f), 1.6f); dl->AddCircleFilled(uc, 4.0f, u32(t.vurgu)); dl->AddCircleFilled(uc, 6.5f, u32(t.vurgu, 0.25f * (1.0f + h))); // merkez değer char buf[32]; std::snprintf(buf, sizeof(buf), "%.1f°", *derece); metniOrtala(dl, {m.x, m.y - 4.0f}, 15.0f, u32(t.metin), buf); metniOrtala(dl, {m.x, m.y + 12.0f}, 10.0f, u32(t.metinSoluk), etiket); ImGui::PopID(); return degisti; } int yonGulu(float boyut) { const Tema& t = tema(); const ImVec2 p = ImGui::GetCursorScreenPos(); const float r = boyut * 0.5f; const ImVec2 m{p.x + r, p.y + r}; ImGui::InvisibleButton("##yongulu", {boyut, boyut}); const bool tik = ImGui::IsItemClicked(); const ImVec2 fare = ImGui::GetMousePos(); const float dx = fare.x - m.x, dy = fare.y - m.y; const float uzak = std::sqrt(dx * dx + dy * dy); const float rIc = r * 0.34f; int hover = -1; if (ImGui::IsItemHovered()) { if (uzak < rIc) hover = 0; // İzo else if (uzak <= r) { float a = std::atan2(dy, dx) + PI * 0.25f; // sektörler 45° kaydırık while (a < 0) a += 2 * PI; const int s = (int)(a / (PI * 0.5f)) % 4; // 0 sağ,1 alt,2 sol,3 üst hover = s == 3 ? 1 : s == 0 ? 2 : s == 1 ? 3 : 4; // Üst/Sağ/Ön(alt)/Sol } } ImDrawList* dl = ImGui::GetWindowDrawList(); static const char* AD[5] = {"İzo", "Üst", "Sağ", "Ön", "Sol"}; // 4 sektör (üst=1, sağ=2, alt(Ön)=3, sol=4) const float basAci[4] = {-PI * 0.75f, -PI * 0.25f, PI * 0.25f, PI * 0.75f}; const int sektorYon[4] = {1, 2, 3, 4}; for (int i = 0; i < 4; ++i) { const bool h = hover == sektorYon[i]; const float a0 = basAci[i] + 0.03f, a1 = basAci[i] + PI * 0.5f - 0.03f; dl->PathClear(); dl->PathArcTo(m, r - 1.5f, a0, a1, 20); dl->PathArcTo(m, rIc + 2.0f, a1, a0, 20); dl->PathFillConcave(u32(karisim(t.panel, t.vurgu, h ? 0.5f : 0.14f), 0.95f)); dl->PathArcTo(m, r - 1.5f, a0, a1, 20); dl->PathStroke(u32(t.vurgu, h ? 0.9f : 0.25f), 0, h ? 2.0f : 1.0f); const float orta = (a0 + a1) * 0.5f; metniOrtala(dl, {m.x + std::cos(orta) * (rIc + r) * 0.5f, m.y + std::sin(orta) * (rIc + r) * 0.5f}, 12.0f, u32(h ? t.metin : t.metinSoluk), AD[sektorYon[i]]); } // merkez İzo (altıgen his: daire + iç daire) const bool hIzo = hover == 0; dl->AddCircleFilled(m, rIc, u32(karisim(t.panel, t.vurgu2, hIzo ? 0.5f : 0.18f), 0.97f)); dl->AddCircle(m, rIc, u32(t.vurgu2, hIzo ? 0.95f : 0.35f), 0, hIzo ? 2.0f : 1.2f); if (hIzo) for (int i = 1; i <= 3; ++i) dl->AddCircle(m, rIc + i * 1.8f, u32(t.vurgu2, 0.16f * t.glowSiddet / i), 0, 2.0f); metniOrtala(dl, m, 12.0f, u32(hIzo ? t.metin : t.metinSoluk), AD[0]); return (tik && hover >= 0) ? hover : -1; } bool aralikSlider(const char* etiket, float* mn, float* mx, float altSinir, float ustSinir, const char* fmt) { const Tema& t = tema(); ImGui::PushID(etiket); if (etiket[0] && etiket[0] != '#') { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted(etiket); ImGui::SameLine(); } const float gen = ImGui::GetContentRegionAvail().x; const float yk = ImGui::GetFrameHeight(); const ImVec2 p = ImGui::GetCursorScreenPos(); ImGui::InvisibleButton("##aralik", {gen, yk}); const ImGuiID id = ImGui::GetItemID(); const bool aktif = ImGui::IsItemActive(); const bool hover = ImGui::IsItemHovered(); const float aralik = ustSinir - altSinir; const float ray0 = p.x + 8.0f, ray1 = p.x + gen - 8.0f; const float cy = p.y + yk * 0.5f; auto konum = [&](float v) { return ray0 + (ray1 - ray0) * ImClamp((v - altSinir) / aralik, 0.0f, 1.0f); }; // hangi tutamaç: sürükleme başında yakına kilitlen (durum: ImGuiStorage) ImGuiStorage* depo = ImGui::GetStateStorage(); const ImGuiID tutKey = id + 100; bool degisti = false; if (aktif) { int tut = depo->GetInt(tutKey, -1); const float fx = ImGui::GetMousePos().x; if (tut < 0) { tut = std::fabs(fx - konum(*mn)) <= std::fabs(fx - konum(*mx)) ? 0 : 1; depo->SetInt(tutKey, tut); } float v = altSinir + (fx - ray0) / (ray1 - ray0) * aralik; v = ImClamp(v, altSinir, ustSinir); if (tut == 0) { if (v > *mx) v = *mx; if (v != *mn) { *mn = v; degisti = true; } } else { if (v < *mn) v = *mn; if (v != *mx) { *mx = v; degisti = true; } } } else { depo->SetInt(tutKey, -1); } const float h = animf(id, (hover || aktif) ? 1.0f : 0.0f, 12.0f); ImDrawList* dl = ImGui::GetWindowDrawList(); // ray + neon dolgu dl->AddRectFilled({ray0, cy - 2.5f}, {ray1, cy + 2.5f}, u32(t.cerceve), 2.5f); const float x0 = konum(*mn), x1 = konum(*mx); const int vtx0 = dl->VtxBuffer.Size; dl->AddRectFilled({x0, cy - 2.5f}, {x1, cy + 2.5f}, u32(t.vurgu), 2.5f); ImGui::ShadeVertsLinearColorGradientKeepAlpha(dl, vtx0, dl->VtxBuffer.Size, {x0, cy}, {x1, cy}, u32(t.vurgu), u32(t.vurgu2)); dl->AddRectFilled({x0, cy - 4.0f}, {x1, cy + 4.0f}, u32(t.vurgu, 0.10f * t.glowSiddet), 4.0f); // tutamaçlar for (int i = 0; i < 2; ++i) { const float x = i == 0 ? x0 : x1; const ImVec4& rk = i == 0 ? t.vurgu : t.vurgu2; dl->AddCircleFilled({x, cy}, 7.0f + h, u32(rk, 0.25f)); dl->AddCircleFilled({x, cy}, 5.5f, u32(t.metin)); dl->AddCircle({x, cy}, 5.5f, u32(rk), 0, 1.5f); } // değer balonları (hover/sürüklemede) if (h > 0.15f) { char buf[48]; std::snprintf(buf, sizeof(buf), fmt, *mn); metniOrtala(dl, {x0, cy - 14.0f - 4.0f * h}, 11.0f, u32(t.metin, h), buf); std::snprintf(buf, sizeof(buf), fmt, *mx); metniOrtala(dl, {x1, cy - 14.0f - 4.0f * h}, 11.0f, u32(t.metin, h), buf); } ImGui::PopID(); return degisti; } void sparkGrafik(const char* kimlik, const float* degerler, int sayi, float genislik, float yukseklik) { if (sayi < 2) return; const Tema& t = tema(); if (genislik <= 0) genislik = ImGui::GetContentRegionAvail().x; const ImVec2 p = ImGui::GetCursorScreenPos(); ImGui::PushID(kimlik); ImGui::InvisibleButton("##spark", {genislik, yukseklik}); const bool hover = ImGui::IsItemHovered(); ImGui::PopID(); float mn = degerler[0], mx = degerler[0]; for (int i = 1; i < sayi; ++i) { mn = ImMin(mn, degerler[i]); mx = ImMax(mx, degerler[i]); } if (mx - mn < 1e-6f) mx = mn + 1.0f; ImDrawList* dl = ImGui::GetWindowDrawList(); dl->AddRectFilled(p, {p.x + genislik, p.y + yukseklik}, u32(t.panel, 0.55f), t.yuvarlak); auto nokta = [&](int i) { return ImVec2{p.x + 6.0f + (genislik - 12.0f) * (float)i / (float)(sayi - 1), p.y + yukseklik - 6.0f - (yukseklik - 12.0f) * (degerler[i] - mn) / (mx - mn)}; }; // alan dolgusu (dikey gradient, çizgiden zemine) for (int i = 0; i + 1 < sayi; ++i) { const ImVec2 a = nokta(i), b = nokta(i + 1); dl->AddQuadFilled(a, b, {b.x, p.y + yukseklik - 3.0f}, {a.x, p.y + yukseklik - 3.0f}, u32(t.vurgu, 0.10f)); } // glow alt katman + ana çizgi for (int i = 0; i + 1 < sayi; ++i) dl->AddLine(nokta(i), nokta(i + 1), u32(t.vurgu, 0.20f * t.glowSiddet), 4.5f); for (int i = 0; i + 1 < sayi; ++i) dl->AddLine(nokta(i), nokta(i + 1), u32(karisim(t.vurgu, t.vurgu2, (float)i / (float)(sayi - 1)), 0.95f), 1.8f); // son nokta nabzı const ImVec2 son = nokta(sayi - 1); dl->AddCircleFilled(son, 3.0f, u32(t.vurgu2)); dl->AddCircleFilled(son, 3.0f + 4.0f * nabiz(1.4f), u32(t.vurgu2, 0.22f)); // hover: en yakın değer etiketi if (hover) { const float fx = ImGui::GetMousePos().x; int yakin = 0; for (int i = 1; i < sayi; ++i) if (std::fabs(nokta(i).x - fx) < std::fabs(nokta(yakin).x - fx)) yakin = i; const ImVec2 n = nokta(yakin); dl->AddLine({n.x, p.y + 3.0f}, {n.x, p.y + yukseklik - 3.0f}, u32(t.metin, 0.25f), 1.0f); dl->AddCircleFilled(n, 3.5f, u32(t.metin)); char buf[32]; std::snprintf(buf, sizeof(buf), "%.2f", degerler[yakin]); metniOrtala(dl, {n.x, p.y - 8.0f}, 11.0f, u32(t.metin), buf); } } void koordinatCip(const float v[3], const char* baslik) { const Tema& t = tema(); static const char* AD[3] = {"X", "Y", "Z"}; char parca[3][32]; float toplamG = 12.0f; ImFont* f = ImGui::GetFont(); const float fs = 13.0f; float basG = 0.0f; if (baslik) { basG = f->CalcTextSizeA(fs, FLT_MAX, 0.0f, baslik).x + 10.0f; toplamG += basG; } float pg[3]; for (int i = 0; i < 3; ++i) { std::snprintf(parca[i], sizeof(parca[i]), "%s %.3f", AD[i], v[i]); pg[i] = f->CalcTextSizeA(fs, FLT_MAX, 0.0f, parca[i]).x; toplamG += pg[i] + 14.0f; } const ImVec2 p = ImGui::GetCursorScreenPos(); const float yk = 24.0f; ImGui::Dummy({toplamG, yk}); ImDrawList* dl = ImGui::GetWindowDrawList(); dl->AddRectFilled(p, {p.x + toplamG, p.y + yk}, u32(t.panel, 0.92f), yk * 0.5f); dl->AddRect(p, {p.x + toplamG, p.y + yk}, u32(t.vurgu, 0.30f), yk * 0.5f, 0, 1.0f); float x = p.x + 10.0f; const float cy = p.y + yk * 0.5f; if (baslik) { dl->AddText(f, fs, {x, cy - fs * 0.55f}, u32(t.metinSoluk), baslik); x += basG; } for (int i = 0; i < 3; ++i) { dl->AddRectFilled({x, p.y + 5.0f}, {x + 3.0f, p.y + yk - 5.0f}, u32(EKSEN[i]), 1.5f); dl->AddText(f, fs, {x + 7.0f, cy - fs * 0.55f}, u32(karisim(t.metin, EKSEN[i], 0.25f)), parca[i]); x += pg[i] + 14.0f; } } int zamanSeridi(const char* kimlik, const char* const* etiketler, int sayi, int secili) { if (sayi <= 0) return -1; const Tema& t = tema(); const float gen = ImGui::GetContentRegionAvail().x; const float yk = 52.0f; const ImVec2 p = ImGui::GetCursorScreenPos(); ImGui::PushID(kimlik); ImGui::InvisibleButton("##serit", {gen, yk}); const bool hover = ImGui::IsItemHovered(); const bool tik = ImGui::IsItemClicked(); ImGui::PopID(); const float cy = p.y + 18.0f; auto nx = [&](int i) { return sayi == 1 ? p.x + gen * 0.5f : p.x + 18.0f + (gen - 36.0f) * (float)i / (float)(sayi - 1); }; int hoverIdx = -1; if (hover) { const ImVec2 fare = ImGui::GetMousePos(); for (int i = 0; i < sayi; ++i) if (std::fabs(fare.x - nx(i)) < 16.0f && std::fabs(fare.y - cy) < 16.0f) hoverIdx = i; } ImDrawList* dl = ImGui::GetWindowDrawList(); // bağlantı hattı: geçmiş neon, gelecek soluk const float sx = secili >= 0 ? nx(ImMin(secili, sayi - 1)) : p.x; dl->AddLine({nx(0), cy}, {sx, cy}, u32(t.vurgu, 0.20f * t.glowSiddet), 5.0f); dl->AddLine({nx(0), cy}, {sx, cy}, u32(t.vurgu, 0.9f), 2.0f); if (secili < sayi - 1) dl->AddLine({sx, cy}, {nx(sayi - 1), cy}, u32(t.metinSoluk, 0.35f), 1.5f); ImFont* f = ImGui::GetFont(); for (int i = 0; i < sayi; ++i) { const float x = nx(i); const bool s = i == secili, h = i == hoverIdx; const bool gecmis = secili >= 0 && i <= secili; const ImVec4 renk = s ? t.vurgu2 : gecmis ? t.vurgu : t.metinSoluk; if (s) dl->AddCircleFilled({x, cy}, 8.0f + 3.0f * nabiz(1.2f), u32(t.vurgu2, 0.20f)); if (h) dl->AddCircleFilled({x, cy}, 10.0f, u32(t.vurgu, 0.18f)); dl->AddCircleFilled({x, cy}, s ? 6.5f : 5.0f, u32(karisim(t.panel, renk, 0.65f))); dl->AddCircle({x, cy}, s ? 6.5f : 5.0f, u32(renk, 0.95f), 0, s ? 2.0f : 1.4f); if (etiketler && etiketler[i]) { const float fs = 11.0f; const ImVec2 mb = f->CalcTextSizeA(fs, FLT_MAX, 0.0f, etiketler[i]); dl->AddText(f, fs, {x - mb.x * 0.5f, cy + 12.0f}, u32(s || h ? t.metin : t.metinSoluk), etiketler[i]); } } return (tik && hoverIdx >= 0) ? hoverIdx : -1; } bool takimKarti(const char* baslik, const char* altyazi, const Simge& simge, bool secili, ImVec2 boyut) { const Tema& t = tema(); if (boyut.y <= 0) boyut.y = boyut.x * 0.78f; const bool tik = ImGui::InvisibleButton(baslik, boyut); const ImGuiID id = ImGui::GetItemID(); const bool hover = ImGui::IsItemHovered(); const float h = animf(id, hover ? 1.0f : 0.0f, 12.0f); const float kalk = 3.0f * h; // hover'da yükselme const ImVec2 mn0 = ImGui::GetItemRectMin(), mx0 = ImGui::GetItemRectMax(); const ImVec2 mn{mn0.x, mn0.y - kalk}, mx{mx0.x, mx0.y - kalk}; ImDrawList* dl = ImGui::GetWindowDrawList(); dl->AddRectFilled({mn0.x + 3, mn0.y + 4}, {mx0.x + 3, mx0.y + 4}, u32({0, 0, 0, 1}, t.koyu ? 0.35f : 0.15f), t.yuvarlak + 2); if (secili || h > 0.05f) glowCerceve(dl, mn, mx, secili ? t.vurgu2 : t.vurgu, t.yuvarlak + 2, secili ? 1.0f : 0.6f * h); const int vtx0 = dl->VtxBuffer.Size; dl->AddRectFilled(mn, mx, u32(t.panel), t.yuvarlak + 2); ImGui::ShadeVertsLinearColorGradientKeepAlpha( dl, vtx0, dl->VtxBuffer.Size, mn, {mn.x, mx.y}, u32(karisim(t.panel, secili ? t.vurgu2 : t.vurgu, 0.10f + 0.08f * h)), u32(karisim(t.panel, {0, 0, 0, 1}, t.koyu ? 0.25f : 0.02f))); dl->AddRect(mn, mx, u32(secili ? t.vurgu2 : t.vurgu, 0.25f + 0.55f * (secili ? 1.0f : h)), t.yuvarlak + 2, 0, secili ? 1.8f : 1.2f); // simge alanı (üst %55) — resim/SVG/emoji hepsi simgeCiz'den const float simgeYuk = (mx.y - mn.y) * 0.55f; const ImVec2 sm{(mn.x + mx.x) * 0.5f, mn.y + simgeYuk * 0.55f}; if (!simge.bosMu()) simgeCiz(dl, simge, sm, ImMin(simgeYuk * 0.8f, (mx.x - mn.x) * 0.5f), u32(t.metin)); // başlık + altyazı ImFont* f = ImGui::GetFont(); const ImVec2 bb = f->CalcTextSizeA(14.0f, FLT_MAX, 0.0f, baslik); dl->AddText(f, 14.0f, {(mn.x + mx.x - bb.x) * 0.5f, mn.y + simgeYuk + 4.0f}, u32(t.metin), baslik); if (altyazi) { const ImVec2 ab = f->CalcTextSizeA(11.0f, FLT_MAX, 0.0f, altyazi); dl->AddText(f, 11.0f, {(mn.x + mx.x - ab.x) * 0.5f, mn.y + simgeYuk + 22.0f}, u32(t.metinSoluk), altyazi); } return tik; } } // namespace fiko::ui