#include "fiko/dokum/dokum_kurulum.hpp" #include #include #include "fiko/dokum/malzeme.hpp" #include "fiko/dokum/voxel_kafes.hpp" namespace fiko::dokum { namespace { constexpr f64 PI = 3.14159265358979; inline Vec3d v3d(Vec3f v) { return {v.x, v.y, v.z}; } // Eksene dik ortonormal taban (silindir/koni halka üretimi). void dikTaban(Vec3f eksen, Vec3f& u, Vec3f& v) { const Vec3f e = eksen.birim(); const Vec3f ref = std::abs(e.z) < 0.9f ? Vec3f{0, 0, 1} : Vec3f{1, 0, 0}; u = e.capraz(ref).birim(); v = e.capraz(u).birim(); } // Kafes indeks aralığı: AABB'yi (mesh birimi) hücre indekslerine kırp. struct IdxAralik { i32 i0, i1, j0, j1, k0, k1; bool bos; }; IdxAralik idxAralik(const VoxelKafes& kafes, Vec3d mn, Vec3d mx) { IdxAralik r{}; const Vec3d kok = kafes.merkez(0, 0, 0); // merkez(1,0,0)-merkez(0,0,0) = mesh hücre boyutu (birimden bağımsız). const f64 hm = kafes.nx() > 1 ? (kafes.merkez(1, 0, 0).x - kok.x) : (kafes.sinir().azami.x - kafes.sinir().asgari.x); auto idx = [&](f64 v, f64 k0) { return (i32)std::floor((v - k0) / hm); }; const Vec3d o{kok.x - 0.5 * hm, kok.y - 0.5 * hm, kok.z - 0.5 * hm}; // grid orijini r.i0 = std::max(0, idx(mn.x, o.x)); r.i1 = std::min((i32)kafes.nx() - 1, idx(mx.x, o.x)); r.j0 = std::max(0, idx(mn.y, o.y)); r.j1 = std::min((i32)kafes.ny() - 1, idx(mx.y, o.y)); r.k0 = std::max(0, idx(mn.z, o.z)); r.k1 = std::min((i32)kafes.nz() - 1, idx(mx.z, o.z)); r.bos = r.i0 > r.i1 || r.j0 > r.j1 || r.k0 > r.k1; return r; } // Nokta silindir içinde mi (taban + eksen·[0,boy], yarıçap r). inline bool silindirIcinde(Vec3d p, Vec3d taban, Vec3d eksen, f64 boy, f64 r) { const Vec3d d = p - taban; const f64 t = d.nokta(eksen); if (t < 0.0 || t > boy) return false; const Vec3d rad = d - eksen * t; return rad.uzunlukKare() <= r * r; } // Nokta doğru parçasına uzaklık karesi. inline f64 parcaUzaklikKare(Vec3d p, Vec3d a, Vec3d b) { const Vec3d ab = b - a; const f64 l2 = ab.uzunlukKare(); const f64 t = l2 > 1e-18 ? std::clamp((p - a).nokta(ab) / l2, 0.0, 1.0) : 0.0; return (p - (a + ab * t)).uzunlukKare(); } // Sınır kabuğunu yenile: eski Sinir sıfırlanır, metale komşu Dis → Sinir. void sinirYenile(VoxelKafes& kafes) { const u32 nx = kafes.nx(), ny = kafes.ny(), nz = kafes.nz(); for (auto& t : kafes.tip) if (t == HucreTip::Sinir) t = HucreTip::Dis; auto ic = [&](i32 i, i32 j, i32 k) -> bool { if (i < 0 || j < 0 || k < 0 || i >= (i32)nx || j >= (i32)ny || k >= (i32)nz) return false; return kafes.tip[kafes.indeks(i, j, k)] == HucreTip::Ic; }; for (u32 k = 0; k < nz; ++k) for (u32 j = 0; j < ny; ++j) for (u32 i = 0; i < nx; ++i) { const u32 idx = kafes.indeks(i, j, k); if (kafes.tip[idx] != HucreTip::Dis) continue; if (ic(i - 1, j, k) || ic(i + 1, j, k) || ic(i, j - 1, k) || ic(i, j + 1, k) || ic(i, j, k - 1) || ic(i, j, k + 1)) kafes.tip[idx] = HucreTip::Sinir; } } // Mesh yardımcıları: üçgen ekle (per-yüz normal + renk). void ucgenEkle(MeshVeri& m, Vec3f a, Vec3f b, Vec3f c, Vec3f renk) { const Vec3f n = (b - a).capraz(c - a).birim(); const u32 t = (u32)m.vertexler.size(); for (const Vec3f& p : {a, b, c}) { Vertex v{}; v.konum[0] = p.x; v.konum[1] = p.y; v.konum[2] = p.z; v.normal[0] = n.x; v.normal[1] = n.y; v.normal[2] = n.z; v.renk[0] = renk.x; v.renk[1] = renk.y; v.renk[2] = renk.z; v.altKimlik = 1; m.vertexler.push_back(v); m.sinir.genislet(p); } m.indeksler.push_back(t); m.indeksler.push_back(t + 1); m.indeksler.push_back(t + 2); } // Kapalı silindir gövdesi (yan + iki kapak) — besleyici/tüp segmenti ortak. void silindirUcgenle(MeshVeri& m, Vec3f taban, Vec3f eksen, f32 r, f32 boy, Vec3f renk, bool kapaklar) { const Vec3f e = eksen.birim(); Vec3f u, v; dikTaban(e, u, v); const int seg = 16; const Vec3f tepe = taban + e * boy; for (int i = 0; i < seg; ++i) { const f32 a0 = (f32)(2.0 * PI * i / seg), a1 = (f32)(2.0 * PI * (i + 1) / seg); const Vec3f r0 = u * std::cos(a0) * r + v * std::sin(a0) * r; const Vec3f r1 = u * std::cos(a1) * r + v * std::sin(a1) * r; const Vec3f p00 = taban + r0, p01 = taban + r1, p10 = tepe + r0, p11 = tepe + r1; ucgenEkle(m, p00, p01, p11, renk); ucgenEkle(m, p00, p11, p10, renk); if (kapaklar) { ucgenEkle(m, taban, p01, p00, renk); ucgenEkle(m, tepe, p10, p11, renk); } } } } // namespace // --------------------------------------------------------------------------- // Kafes entegrasyonu // --------------------------------------------------------------------------- void kurulumSinirGenislet(Aabbd& sinir, const DokumKurulum& k) { const Vec3d yukari = -v3d(k.graviteYon).birim(); for (const auto& b : k.besleyiciler) { if (!b.aktif) continue; const Vec3d t = v3d(b.konum); const Vec3d u = t + yukari * b.yukseklikMm; const f64 r = 0.5 * b.capMm; const Vec3d pay{r, r, r}; sinir.genislet(t - pay); sinir.genislet(t + pay); sinir.genislet(u - pay); sinir.genislet(u + pay); } for (const auto& y : k.yolluklar) { if (!y.aktif) continue; const f64 r = std::sqrt(std::max(y.kesitMm2, 0.0) / PI); const Vec3d pay{r, r, r}; for (const Vec3f& p : y.guzergah) { sinir.genislet(v3d(p) - pay); sinir.genislet(v3d(p) + pay); } } } void kurulumKafeseUygula(VoxelKafes& kafes, const DokumKurulum& k) { if (!kafes.gecerliMi()) return; bool degisti = false; const Vec3d yukari = -v3d(k.graviteYon).birim(); // Besleyici silindirleri → Ic. for (const auto& b : k.besleyiciler) { if (!b.aktif) continue; const Vec3d taban = v3d(b.konum); const f64 r = 0.5 * b.capMm, boy = b.yukseklikMm; Aabbd s; s.genislet(taban - Vec3d{r, r, r}); s.genislet(taban + Vec3d{r, r, r}); const Vec3d uc = taban + yukari * boy; s.genislet(uc - Vec3d{r, r, r}); s.genislet(uc + Vec3d{r, r, r}); const IdxAralik a = idxAralik(kafes, s.asgari, s.azami); if (a.bos) continue; for (i32 kk = a.k0; kk <= a.k1; ++kk) for (i32 j = a.j0; j <= a.j1; ++j) for (i32 i = a.i0; i <= a.i1; ++i) if (silindirIcinde(kafes.merkez(i, j, kk), taban, yukari, boy, r)) { kafes.tip[kafes.indeks(i, j, kk)] = HucreTip::Ic; degisti = true; } } // Yolluk kanalları → Ic (güzergâh tüpü). for (const auto& y : k.yolluklar) { if (!y.aktif || y.guzergah.size() < 2) continue; const f64 r = std::sqrt(std::max(y.kesitMm2, 0.0) / PI); const f64 r2 = r * r; for (size_t s = 0; s + 1 < y.guzergah.size(); ++s) { const Vec3d a = v3d(y.guzergah[s]), b = v3d(y.guzergah[s + 1]); Aabbd sn; sn.genislet(a - Vec3d{r, r, r}); sn.genislet(a + Vec3d{r, r, r}); sn.genislet(b - Vec3d{r, r, r}); sn.genislet(b + Vec3d{r, r, r}); const IdxAralik ar = idxAralik(kafes, sn.asgari, sn.azami); if (ar.bos) continue; for (i32 kk = ar.k0; kk <= ar.k1; ++kk) for (i32 j = ar.j0; j <= ar.j1; ++j) for (i32 i = ar.i0; i <= ar.i1; ++i) if (parcaUzaklikKare(kafes.merkez(i, j, kk), a, b) <= r2) { kafes.tip[kafes.indeks(i, j, kk)] = HucreTip::Ic; degisti = true; } } } if (degisti) sinirYenile(kafes); } std::vector chillHtcHaritasi(const VoxelKafes& kafes, const DokumKurulum& k) { bool var = false; for (const auto& c : k.sogutucular) if (c.aktif && c.htc > 0) { var = true; break; } if (!var || !kafes.gecerliMi()) return {}; std::vector h(kafes.hucreSayi(), 0.0); for (const auto& c : k.sogutucular) { if (!c.aktif || c.htc <= 0) continue; const Vec3d m = v3d(c.konum); const f64 r2 = c.yaricapMm * c.yaricapMm; const Vec3d pay{c.yaricapMm, c.yaricapMm, c.yaricapMm}; const IdxAralik a = idxAralik(kafes, m - pay, m + pay); if (a.bos) continue; for (i32 kk = a.k0; kk <= a.k1; ++kk) for (i32 j = a.j0; j <= a.j1; ++j) for (i32 i = a.i0; i <= a.i1; ++i) { const u32 idx = kafes.indeks(i, j, kk); if ((kafes.merkez(i, j, kk) - m).uzunlukKare() <= r2) h[idx] = std::max(h[idx], c.htc); } } return h; } u32 enYakinMetalHucre(const VoxelKafes& kafes, Vec3f konumMm) { if (!kafes.gecerliMi()) return static_cast(-1); const Vec3d p = v3d(konumMm); u32 enIyi = static_cast(-1); f64 enKisa = 1e300; const u32 nx = kafes.nx(), ny = kafes.ny(), nz = kafes.nz(); for (u32 k = 0; k < nz; ++k) for (u32 j = 0; j < ny; ++j) for (u32 i = 0; i < nx; ++i) { const u32 idx = kafes.indeks(i, j, k); if (kafes.tip[idx] == HucreTip::Dis) continue; const f64 d = (kafes.merkez(i, j, k) - p).uzunlukKare(); if (d < enKisa) { enKisa = d; enIyi = idx; } } return enIyi; } // --------------------------------------------------------------------------- // Mühendislik yardımcıları (docs/dokum_kurulum_arastirma.md) // --------------------------------------------------------------------------- f64 dokumSuresiOner(const Alasim& a, f64 kutleKg, f64 etKalinlikMm) { const f64 W = std::max(kutleKg, 0.01); const f64 T = std::max(etKalinlikMm, 1.0); if (a.tLikidus > 1400.0) // çelik: t=(2.4335−0.3953·log₁₀W)√W return std::max(0.5, (2.4335 - 0.3953 * std::log10(W)) * std::sqrt(W)); if (a.tLikidus > 1000.0) // gri demir: t=K(1.41+T/14.59)√W, K≈1 return std::max(0.5, (1.41 + T / 14.59) * std::sqrt(W)); return std::max(0.5, 2.4 * std::sqrt(W)); // hafif alaşım: t=K√W, K≈2.4 } f64 girisOrtalamaHiz(const DokumGirisi& g, f64 yogunluk) { if (g.sureS <= 0 || g.capMm <= 0 || yogunluk <= 0) return 0.0; const f64 Q = (g.kutleKg / yogunluk) / g.sureS; // [m³/s] const f64 r = 0.5 * g.capMm * 1e-3; // [m] return Q / (PI * r * r); // [m/s] } f64 kritikGirisHiz(const Alasim& a) { if (a.tLikidus > 1400.0) return 0.5; // çelik if (a.tLikidus > 1000.0) return 1.0; // gri demir (pratik tolere) return 0.5; // Al/Mg (Campbell 0.4–0.6) } void dokumSicaklikBandi(const Alasim& a, f64& mn, f64& mx) { if (a.tLikidus > 1400.0) { mn = 1550; mx = 1620; return; } // çelik if (a.tLikidus > 1000.0) { mn = 1300; mx = 1400; return; } // gri demir if (a.tLikidus > 500.0 && a.tLikidus < 700.0) { mn = 680; mx = 750; return; } // Al-Si mn = a.tLikidus + 40.0; mx = a.tLikidus + 150.0; } bool besleyiciHacimYeterli(f64 parcaHacimMm3, f64 riserHacimMm3, f64 eta, f64 beta) { return riserHacimMm3 * eta >= beta * (parcaHacimMm3 + riserHacimMm3) - 1e-9; } // --------------------------------------------------------------------------- // Sahne önizleme mesh üreticileri // --------------------------------------------------------------------------- MeshVeri kurulumKoniMesh(Vec3f uc, Vec3f yon, f32 capMm, f32 boyMm, Vec3f renk) { MeshVeri m; m.yuzSayi = 1; const Vec3f e = yon.uzunlukKare() > 1e-12f ? yon.birim() : Vec3f{0, 0, -1}; const Vec3f taban = uc - e * boyMm; // koni tepesi akış noktasında, taban geride Vec3f u, v; dikTaban(e, u, v); const f32 r = 0.5f * capMm; const int seg = 12; for (int i = 0; i < seg; ++i) { const f32 a0 = (f32)(2.0 * PI * i / seg), a1 = (f32)(2.0 * PI * (i + 1) / seg); const Vec3f p0 = taban + u * std::cos(a0) * r + v * std::sin(a0) * r; const Vec3f p1 = taban + u * std::cos(a1) * r + v * std::sin(a1) * r; ucgenEkle(m, p0, p1, uc, renk); // yan ucgenEkle(m, taban, p1, p0, renk); // taban kapağı } return m; } MeshVeri kurulumKureMesh(Vec3f merkez, f32 yaricapMm, Vec3f renk) { MeshVeri m; m.yuzSayi = 1; const int halka = 8, dilim = 12; auto nokta = [&](int h, int d) -> Vec3f { const f32 fi = (f32)(PI * h / halka), te = (f32)(2.0 * PI * d / dilim); return merkez + Vec3f{std::sin(fi) * std::cos(te), std::sin(fi) * std::sin(te), std::cos(fi)} * yaricapMm; }; for (int h = 0; h < halka; ++h) for (int d = 0; d < dilim; ++d) { const Vec3f a = nokta(h, d), b = nokta(h + 1, d), c = nokta(h + 1, d + 1), e = nokta(h, d + 1); ucgenEkle(m, a, b, c, renk); ucgenEkle(m, a, c, e, renk); } return m; } MeshVeri kurulumSilindirMesh(Vec3f taban, Vec3f eksen, f32 capMm, f32 boyMm, Vec3f renk) { MeshVeri m; m.yuzSayi = 1; silindirUcgenle(m, taban, eksen, 0.5f * capMm, boyMm, renk, true); return m; } MeshVeri kurulumTupMesh(const std::vector& guzergah, f32 yaricapMm, Vec3f renk) { MeshVeri m; m.yuzSayi = 1; for (size_t i = 0; i + 1 < guzergah.size(); ++i) { const Vec3f a = guzergah[i], b = guzergah[i + 1]; const Vec3f d = b - a; const f32 boy = d.uzunluk(); if (boy < 1e-4f) continue; silindirUcgenle(m, a, d, yaricapMm, boy, renk, true); } return m; } namespace { // Eksen hizalı dolu kutu — dışa bakan normaller (ucgenEkle sargı yönünden). void kutuUcgenle(MeshVeri& m, Vec3f mn, Vec3f mx, Vec3f renk) { const Vec3f p000{mn.x, mn.y, mn.z}, p100{mx.x, mn.y, mn.z}; const Vec3f p010{mn.x, mx.y, mn.z}, p110{mx.x, mx.y, mn.z}; const Vec3f p001{mn.x, mn.y, mx.z}, p101{mx.x, mn.y, mx.z}; const Vec3f p011{mn.x, mx.y, mx.z}, p111{mx.x, mx.y, mx.z}; auto dortgen = [&](Vec3f a, Vec3f b, Vec3f c, Vec3f d) { ucgenEkle(m, a, b, c, renk); ucgenEkle(m, a, c, d, renk); }; dortgen(p000, p010, p110, p100); // alt (-z) dortgen(p001, p101, p111, p011); // üst (+z) dortgen(p000, p100, p101, p001); // ön (-y) dortgen(p010, p011, p111, p110); // arka (+y) dortgen(p000, p001, p011, p010); // sol (-x) dortgen(p100, p110, p111, p101); // sağ (+x) } } // namespace MeshVeri kurulumKutuMesh(Vec3f mn, Vec3f mx, Vec3f renk) { MeshVeri m; m.yuzSayi = 1; if (mx.x > mn.x && mx.y > mn.y && mx.z > mn.z) kutuUcgenle(m, mn, mx, renk); return m; } MeshVeri kurulumCerceveMesh(Vec3f icMn, Vec3f icMx, f32 duvarMm, Vec3f renk) { MeshVeri m; m.yuzSayi = 1; if (!(icMx.x > icMn.x && icMx.y > icMn.y && icMx.z > icMn.z)) return m; const f32 d = std::max(duvarMm, 0.5f); // x duvarları y'de duvar kadar taşar → köşeler kapalı; üst/alt açık kalır. kutuUcgenle(m, {icMn.x - d, icMn.y - d, icMn.z}, {icMn.x, icMx.y + d, icMx.z}, renk); kutuUcgenle(m, {icMx.x, icMn.y - d, icMn.z}, {icMx.x + d, icMx.y + d, icMx.z}, renk); kutuUcgenle(m, {icMn.x, icMn.y - d, icMn.z}, {icMx.x, icMn.y, icMx.z}, renk); kutuUcgenle(m, {icMn.x, icMx.y, icMn.z}, {icMx.x, icMx.y + d, icMx.z}, renk); return m; } } // namespace fiko::dokum