#include "fiko/kutuk/tridexel_motor.hpp" #include #include #include "fiko/kutuk/paralel_havuz.hpp" #include "fiko/kutuk/sdf_motor.hpp" // takimSdf/sweptSdf/etkiKutusu* (analitik, statik) #include "mc_tablolari.hpp" namespace fiko::kutuk { TriDexelMotoru::TriDexelMotoru() = default; // havuz_ tam tip burada TriDexelMotoru::~TriDexelMotoru() = default; namespace { constexpr f64 EPS_SLIVER = 1e-4; // mm — bu inceligin altindaki span/kesik atilir inline bool sonlu(const Vec3d& v) { return std::isfinite(v.x) && std::isfinite(v.y) && std::isfinite(v.z); } // Oct-encode normal <-> u32 (sdf_motor ile ayni sema; bagimsiz kopya — statik yerel). inline u32 octPak(Vec3d n) { const f64 s = std::fabs(n.x) + std::fabs(n.y) + std::fabs(n.z); if (s < 1e-12) return 0u; f64 x = n.x / s, y = n.y / s; if (n.z < 0.0) { const f64 ox = (1.0 - std::fabs(y)) * (x >= 0 ? 1.0 : -1.0); const f64 oy = (1.0 - std::fabs(x)) * (y >= 0 ? 1.0 : -1.0); x = ox; y = oy; } auto q = [](f64 v) -> u32 { v = std::clamp(v, -1.0, 1.0); return (u32)(u16)(i16)std::lround(v * 32767.0); }; return q(x) | (q(y) << 16); } inline Vec3d octAc(u32 p) { if (p == 0u) return {0, 0, 1}; auto d = [](u32 h) { return (f64)(i16)(u16)h / 32767.0; }; f64 x = d(p & 0xFFFFu), y = d(p >> 16); const f64 z = 1.0 - std::fabs(x) - std::fabs(y); if (z < 0.0) { const f64 ox = (1.0 - std::fabs(y)) * (x >= 0 ? 1.0 : -1.0); const f64 oy = (1.0 - std::fabs(x)) * (y >= 0 ? 1.0 : -1.0); x = ox; y = oy; } Vec3d n{x, y, z}; const f64 l2 = n.uzunlukKare(); return (l2 < 1e-20) ? Vec3d{0, 0, 1} : n / std::sqrt(l2); } // 3x3 simetrik QEF cozumu (kofaktor; sdf_motor DC ile ayni yaklasim). bool qefCoz(const f64 A[6], const f64 b[3], f64 x[3]) { const f64 a00 = A[0], a01 = A[1], a02 = A[2], a11 = A[3], a12 = A[4], a22 = A[5]; const f64 c00 = a11 * a22 - a12 * a12; const f64 c01 = a02 * a12 - a01 * a22; const f64 c02 = a01 * a12 - a02 * a11; const f64 det = a00 * c00 + a01 * c01 + a02 * c02; if (std::fabs(det) < 1e-10) return false; const f64 c11 = a00 * a22 - a02 * a02; const f64 c12 = a02 * a01 - a00 * a12; const f64 c22 = a00 * a11 - a01 * a01; const f64 inv = 1.0 / det; x[0] = inv * (c00 * b[0] + c01 * b[1] + c02 * b[2]); x[1] = inv * (c01 * b[0] + c11 * b[1] + c12 * b[2]); x[2] = inv * (c02 * b[0] + c12 * b[1] + c22 * b[2]); return true; } // Eksen d isini icin sabit koordinat eksenleri (u,v). inline void eksenUV(int d, int& ue, int& ve) { if (d == 0) { ue = 1; ve = 2; } // X isini: (y,z) sabit else if (d == 1) { ue = 0; ve = 2; } // Y isini: (x,z) sabit else { ue = 0; ve = 1; } // Z isini: (x,y) sabit } } // namespace f64 TriDexelMotoru::dugum(int eksen, u32 i) const { // Kafes YARIM HUCRE kaydirilir: dugumler stok yuzeyiyle CAKISMAZ → tum // kesisimler kenar ICINDE (dejenere yok) + sinir yuzeyleri kapali mesh verir. const f32 mn[3] = {sinir_.asgari.x, sinir_.asgari.y, sinir_.asgari.z}; return (f64)mn[eksen] - 0.5 * h_ + (f64)i * h_; } // --------------------------------------------------------------------------- // kutukKur — kafes boyutla + stok'tan span'lari ANALITIK doldur // --------------------------------------------------------------------------- bool TriDexelMotoru::kutukKur(const cam::Stok& stok, Aabbf sinir, f64 cozunurluk) { sonHata_.clear(); kuruldu_ = false; kaldirilanHacim_ = 0.0; stok_ = stok; if (!sinir.gecerliMi()) { sonHata_ = "Gecersiz kutuk siniri (min>max veya NaN)."; return false; } if (!(cozunurluk > 1e-9) || !std::isfinite(cozunurluk)) { sonHata_ = "Gecersiz cozunurluk."; return false; } h_ = cozunurluk; sinir_ = sinir; const Vec3f boy = sinir.boyut(); if (boy.x <= 1e-9 || boy.y <= 1e-9 || boy.z <= 1e-9) { nx_ = ny_ = nz_ = 0; for (auto& s : set_) { s.kolon.clear(); s.nu = s.nv = 0; } kuruldu_ = true; // bos kutuk (mesh bos) return true; } auto sayi = [&](f64 len) -> u32 { // +2: yarim-hucre kaydirmali kafes stok'u iki yandan asar (kapali mesh). return std::max(3, (u32)(std::ceil(len / h_)) + 2); }; nx_ = sayi(boy.x); ny_ = sayi(boy.y); nz_ = sayi(boy.z); // Kolon bellek guard (dugum^2 * 3 set): ~64M kolon ustu reddet. const u64 kolonToplam = (u64)ny_ * nz_ + (u64)nx_ * nz_ + (u64)nx_ * ny_; if (kolonToplam > 64ull * 1024 * 1024) { sonHata_ = "Cozunurluk cok yuksek (dexel kolon butcesi asildi)."; nx_ = ny_ = nz_ = 0; return false; } kolonlariKur(); if (!havuz_) havuz_ = std::make_unique(); // kalici havuz (bir kez) kuruldu_ = true; return true; } void TriDexelMotoru::kolonlariKur() { const u32 nuv[3][2] = {{ny_, nz_}, {nx_, nz_}, {nx_, ny_}}; const f64 mn[3] = {(f64)sinir_.asgari.x, (f64)sinir_.asgari.y, (f64)sinir_.asgari.z}; const f64 mx[3] = {(f64)sinir_.azami.x, (f64)sinir_.azami.y, (f64)sinir_.azami.z}; const bool silindir = stok_.tip == cam::StokTip::Silindir; const f64 cx = (mn[0] + mx[0]) * 0.5, cy = (mn[1] + mx[1]) * 0.5; const f64 R = silindir ? stok_.boyut.x * 0.5 : 0.0; for (int d = 0; d < 3; ++d) { DexelSeti& s = set_[d]; s.nu = nuv[d][0]; s.nv = nuv[d][1]; s.kolon.assign((size_t)s.nu * s.nv, {}); int ue, ve; eksenUV(d, ue, ve); // Eksen yonunde stok araligi + uc normalleri (kutu: yuz normali ±d). Vec3d nAlt{0, 0, 0}, nUst{0, 0, 0}; ((f64*)&nAlt)[0] = 0; // (asagida per-tip doldurulur) for (u32 iv = 0; iv < s.nv; ++iv) for (u32 iu = 0; iu < s.nu; ++iu) { const f64 cu = dugum(ue, iu), cv = dugum(ve, iv); // Dugum stok kesitinde mi + eksen boyunca dolu aralik? f64 t0 = mn[d], t1 = mx[d]; bool dolu = cu >= mn[ue] - 1e-9 && cu <= mx[ue] + 1e-9 && cv <= mx[ve] + 1e-9 && cv >= mn[ve] - 1e-9; Vec3d n0{0, 0, 0}, n1{0, 0, 0}; ((f64*)&n0.x)[d % 1] = 0; // no-op (okunabilirlik) // Kutu: uc normalleri eksen yuzleri. Vec3d ax{0, 0, 0}; (&ax.x)[d] = 1.0; n0 = Vec3d{-ax.x, -ax.y, -ax.z}; n1 = ax; if (silindir) { // Silindir stok (eksen Z): XY radyal kisit + Z kapaklar. if (d == 2) { const f64 dx = cu - cx, dy = cv - cy; dolu = dolu && (dx * dx + dy * dy) <= R * R + 1e-9; } else { // X/Y isini: radyal kesisim → aralik daralir. const f64 sabit = (d == 0) ? (cu - cy) : (cu - cx); // d=0: u=y; d=1: u=x // dikkat: d=0'da u ekseni y, kesit koordinati y-cy; d=1'de u ekseni x. const f64 diger = (d == 0) ? (0.0) : (0.0); (void)diger; const f64 kk = R * R - sabit * sabit; if (kk <= 0) { dolu = false; } else { const f64 yar = std::sqrt(kk); const f64 merkez = (d == 0) ? cx : cy; t0 = std::max(t0, merkez - yar); t1 = std::min(t1, merkez + yar); // Radyal normal (dista dogru). Vec3d rn{0, 0, 0}; if (d == 0) rn = Vec3d{-yar, sabit, 0}; else rn = Vec3d{sabit, -yar, 0}; const f64 l = std::sqrt(rn.uzunlukKare()); if (l > 1e-12) n0 = rn / l; Vec3d rn1 = (d == 0) ? Vec3d{yar, sabit, 0} : Vec3d{sabit, yar, 0}; const f64 l1 = std::sqrt(rn1.uzunlukKare()); if (l1 > 1e-12) n1 = rn1 / l1; } } } if (dolu && t1 - t0 > EPS_SLIVER) s.kolon[(size_t)iv * s.nu + iu].push_back({(f32)t0, (f32)t1, octPak(n0), octPak(n1)}); } } } // --------------------------------------------------------------------------- // kolondanCikar — [ka,kb] araligini span listesinden cikar (analitik uclar) // --------------------------------------------------------------------------- f64 TriDexelMotoru::kolondanCikar(std::vector& kolon, f64 ka, f64 kb, u32 na, u32 nb) { if (kb - ka <= EPS_SLIVER || kolon.empty()) return 0.0; f64 cikan = 0.0; for (size_t i = 0; i < kolon.size();) { Span& sp = kolon[i]; if (sp.t1 <= ka || sp.t0 >= kb) { ++i; continue; } // kesismiyor if (sp.t0 >= ka - EPS_SLIVER && sp.t1 <= kb + EPS_SLIVER) { cikan += sp.t1 - sp.t0; // span tamamen kesildi kolon.erase(kolon.begin() + (ptrdiff_t)i); continue; } if (sp.t0 < ka && sp.t1 > kb) { // ic bolme → split const Span ust{(f32)kb, sp.t1, nb, sp.n1}; sp.t1 = (f32)ka; sp.n1 = na; cikan += kb - ka; kolon.insert(kolon.begin() + (ptrdiff_t)i + 1, ust); i += 2; continue; } if (sp.t1 > ka && sp.t1 <= kb + EPS_SLIVER) { // ust kismi kesildi cikan += sp.t1 - ka; sp.t1 = (f32)ka; sp.n1 = na; if (sp.t1 - sp.t0 <= EPS_SLIVER) { kolon.erase(kolon.begin() + (ptrdiff_t)i); continue; } ++i; continue; } // alt kismi kesildi cikan += kb - sp.t0; sp.t0 = (f32)kb; sp.n0 = nb; if (sp.t1 - sp.t0 <= EPS_SLIVER) { kolon.erase(kolon.begin() + (ptrdiff_t)i); continue; } ++i; } return cikan; } // --------------------------------------------------------------------------- // eksenKes — bir isin ailesinde ANALITIK carve (bracketing + bisection) // --------------------------------------------------------------------------- f64 TriDexelMotoru::eksenKes(int d, Vec3d a, Vec3d b, const TakimGeo& t, bool nokta, u32 bandV0, u32 bandV1) { DexelSeti& s = set_[d]; if (s.kolon.empty()) return 0.0; int ue, ve; eksenUV(d, ue, ve); Vec3d mn, mx; if (nokta) SdfMotoru::etkiKutusu(b, t, mn, mx); else SdfMotoru::etkiKutusuSwept(a, b, t, mn, mx); // Kolon indeks araligi. auto aralik = [&](int eks, u32 n, u32& i0, u32& i1) -> bool { const f64 lo = (&mn.x)[eks], hi = (&mx.x)[eks]; const f64 o = dugum(eks, 0); f64 fl = (lo - o) / h_, fh = (hi - o) / h_; if (fh < 0 || fl > (f64)(n - 1)) return false; i0 = (u32)std::max(0.0, std::floor(fl)); i1 = (u32)std::min(n - 1, std::ceil(fh)); return i0 <= i1; }; u32 u0, u1, v0, v1; if (!aralik(ue, s.nu, u0, u1) || !aralik(ve, s.nv, v0, v1)) return 0.0; v0 = std::max(v0, bandV0); // paralel gorev v-bant kisiti v1 = std::min(v1, bandV1); if (v0 > v1) return 0.0; const f64 w0 = std::max((f64)(&mn.x)[d], dugum(d, 0) - 2.0 * h_); const f64 w1 = std::min((f64)(&mx.x)[d], dugum(d, (d == 0 ? nx_ : d == 1 ? ny_ : nz_) - 1) + 2.0 * h_); if (w1 - w0 <= EPS_SLIVER) return 0.0; // Supurme SDF (konveks → isin kesisimi TEK aralik). auto f = [&](Vec3d p) -> f64 { return nokta ? SdfMotoru::takimSdf(p, b, t) : SdfMotoru::sweptSdf(p, a, b, t); }; const f64 adim = std::max(h_ * 0.5, 1e-3); const int nAdim = (int)std::min(4096.0, std::ceil((w1 - w0) / adim)) + 1; const f64 grEps = std::max(h_ * 0.05, 1e-4); f64 toplamCikan = 0.0; for (u32 iv = v0; iv <= v1; ++iv) { for (u32 iu = u0; iu <= u1; ++iu) { std::vector& kolon = s.kolon[(size_t)iv * s.nu + iu]; if (kolon.empty()) continue; Vec3d p{0, 0, 0}; (&p.x)[ue] = dugum(ue, iu); (&p.x)[ve] = dugum(ve, iv); auto fw = [&](f64 w) { Vec3d q = p; (&q.x)[d] = w; return f(q); }; // Ornekleme: ilk/son negatif ornegi bul (konveks ⇒ tek blok). int ilkNeg = -1, sonNeg = -1; f64 wIlk = 0, wSon = 0, wOncesi = w0, wSonrasi = w1; for (int k = 0; k < nAdim; ++k) { const f64 w = w0 + (w1 - w0) * (f64)k / (f64)(nAdim - 1); if (fw(w) < 0.0) { if (ilkNeg < 0) { ilkNeg = k; wIlk = w; wOncesi = (k > 0) ? w0 + (w1 - w0) * (f64)(k - 1) / (f64)(nAdim - 1) : w0; } sonNeg = k; wSon = w; } else if (sonNeg >= 0 && sonNeg == k - 1) { wSonrasi = w; } } if (ilkNeg < 0) continue; // isin supurmeyi kesmiyor (veya sliver — atla) if (sonNeg == nAdim - 1) wSonrasi = w1; // Bisection: giris (dis→ic) ve cikis (ic→dis) — ~1e-4 mm. auto bisG = [&](f64 lo, f64 hi) { // f(lo)>=0, f(hi)<0 → kesisim for (int it = 0; it < 30; ++it) { const f64 m = (lo + hi) * 0.5; if (fw(m) < 0.0) hi = m; else lo = m; } return (lo + hi) * 0.5; }; auto bisC = [&](f64 lo, f64 hi) { // f(lo)<0, f(hi)>=0 for (int it = 0; it < 30; ++it) { const f64 m = (lo + hi) * 0.5; if (fw(m) < 0.0) lo = m; else hi = m; } return (lo + hi) * 0.5; }; const f64 giris = (fw(wOncesi) >= 0.0) ? bisG(wOncesi, wIlk) : wOncesi; const f64 cikis = (fw(wSonrasi) >= 0.0) ? bisC(wSon, wSonrasi) : wSonrasi; if (cikis - giris <= EPS_SLIVER) continue; // Analitik normal: -grad(supurmeSdf) (malzeme disari; sdf_motor konvansiyonu). auto normal = [&](f64 w) -> u32 { Vec3d q = p; (&q.x)[d] = w; const Vec3d g{f({q.x + grEps, q.y, q.z}) - f({q.x - grEps, q.y, q.z}), f({q.x, q.y + grEps, q.z}) - f({q.x, q.y - grEps, q.z}), f({q.x, q.y, q.z + grEps}) - f({q.x, q.y, q.z - grEps})}; const f64 l2 = g.uzunlukKare(); if (l2 < 1e-20) return 0u; return octPak(Vec3d{-g.x, -g.y, -g.z} / std::sqrt(l2)); }; toplamCikan += kolondanCikar(kolon, giris, cikis, normal(giris), normal(cikis)); } } return toplamCikan; } void TriDexelMotoru::takimUygula(Vec3d poz, const TakimGeo& takim) { if (!kuruldu_ || nx_ == 0 || !sonlu(poz) || !(takim.cap > 0)) return; TakimGeo t = takim; const f64 e2 = t.eksen.uzunlukKare(); t.eksen = (e2 < 1e-12 || !sonlu(t.eksen)) ? Vec3d{0, 0, 1} : t.eksen / std::sqrt(e2); for (int d = 0; d < 3; ++d) { const f64 cikan = eksenKes(d, poz, poz, t, true, 0, ~0u); if (d == 2) kaldirilanHacim_ += cikan * h_ * h_; // Z kanonik hacim } } void TriDexelMotoru::sweptUygula(Vec3d a, Vec3d b, const TakimGeo& takim) { if (!kuruldu_ || nx_ == 0 || !sonlu(a) || !sonlu(b) || !(takim.cap > 0)) return; TakimGeo t = takim; const f64 e2 = t.eksen.uzunlukKare(); t.eksen = (e2 < 1e-12 || !sonlu(t.eksen)) ? Vec3d{0, 0, 1} : t.eksen / std::sqrt(e2); for (int d = 0; d < 3; ++d) { const f64 cikan = eksenKes(d, a, b, t, false, 0, ~0u); if (d == 2) kaldirilanHacim_ += cikan * h_ * h_; } } // --------------------------------------------------------------------------- // batchUygula — cok-hamle PARALEL carve (3 eksen × v-bant; kolonlar disjoint) // ---------------------------------------------------------------------------- // Her gorev = (eksen, v-bant): TUM hamleleri kendi kolonlarina SIRAYLA uygular → // kolon basina hamle sirasi korunur (span mutasyonu yarissiz), sonuc seri ile // birebir. Hacim per-gorev toplanip tek noktada birlesir. // --------------------------------------------------------------------------- void TriDexelMotoru::batchUygula(const KarveHamle* h, size_t n) { if (!kuruldu_ || nx_ == 0 || n == 0) return; if (!havuz_ || n < 4) { // kucuk yigin → seri (gorev kurulum maliyeti degmez) for (size_t i = 0; i < n; ++i) { if (h[i].ilk) takimUygula(h[i].b, h[i].takim); else if (h[i].kesme) sweptUygula(h[i].a, h[i].b, h[i].takim); } return; } // Hamleleri normalize et (eksen birim + gecersizleri ele). struct HN { Vec3d a, b; TakimGeo t; bool nokta; }; std::vector hs; hs.reserve(n); for (size_t i = 0; i < n; ++i) { if (!h[i].ilk && !h[i].kesme) continue; if (!sonlu(h[i].b) || (!h[i].ilk && !sonlu(h[i].a)) || !(h[i].takim.cap > 0)) continue; HN z; z.t = h[i].takim; const f64 e2 = z.t.eksen.uzunlukKare(); z.t.eksen = (e2 < 1e-12 || !sonlu(z.t.eksen)) ? Vec3d{0, 0, 1} : z.t.eksen / std::sqrt(e2); z.a = h[i].ilk ? h[i].b : h[i].a; z.b = h[i].b; z.nokta = h[i].ilk; hs.push_back(z); } if (hs.empty()) return; // Gorev listesi: (eksen, v-bant). Bant sayisi ≈ 3-4× isci (is-calma dengeli). const unsigned T = havuz_->isciSayisi(); struct Gorev { int d; u32 v0, v1; }; std::vector gorevler; for (int d = 0; d < 3; ++d) { const u32 nv = set_[d].nv; if (nv == 0) continue; const u32 bant = std::max(4, nv / std::max(1u, T)); for (u32 v = 0; v < nv; v += bant) gorevler.push_back({d, v, std::min(nv - 1, v + bant - 1)}); } std::vector hacimler(gorevler.size(), 0.0); havuz_->kos( [&](unsigned g) { const Gorev& gv = gorevler[g]; f64 top = 0.0; for (const HN& m : hs) top += eksenKes(gv.d, m.a, m.b, m.t, m.nokta, gv.v0, gv.v1); if (gv.d == 2) hacimler[g] = top; }, (unsigned)gorevler.size()); f64 top = 0.0; for (f64 v : hacimler) top += v; kaldirilanHacim_ += top * h_ * h_; } bool TriDexelMotoru::zIcinde(u32 i, u32 j, f64 z) const { const DexelSeti& s = set_[2]; const std::vector& k = s.kolon[(size_t)j * s.nu + i]; for (const Span& sp : k) if (z >= sp.t0 - 1e-6 && z <= sp.t1 + 1e-6) return true; return false; } bool TriDexelMotoru::noktaIcerideMi(Vec3d p) const { if (!kuruldu_ || nx_ == 0 || !sonlu(p)) return false; // En yakin Z kolonu (dugum) + span uyeligi. const f64 fx = (p.x - dugum(0, 0)) / h_, fy = (p.y - dugum(1, 0)) / h_; const int i = (int)std::lround(fx), j = (int)std::lround(fy); if (i < 0 || j < 0 || i >= (int)nx_ || j >= (int)ny_) return false; return zIcinde((u32)i, (u32)j, p.z); } void TriDexelMotoru::sifirla() { kaldirilanHacim_ = 0.0; if (kuruldu_ && nx_ > 0) kolonlariKur(); } // --------------------------------------------------------------------------- // meshCikar — Dual Contouring, EXACT Hermite (dexel kesisimleri + normaller) // --------------------------------------------------------------------------- void TriDexelMotoru::meshCikar(MeshVeri& cikti) { cikti.temizle(); if (!kuruldu_ || nx_ < 2 || ny_ < 2 || nz_ < 2) return; const u32 cx = nx_ - 1, cy = ny_ - 1, cz = nz_ - 1; // Kose uyelik kafesi (Z-dexel kanonik): bit basina 1 dugum. PARALEL: j-satirlari // bagimsiz (her kolon yalniz kendi (i,j,*) dugumlerini yazar — disjoint). std::vector ic((size_t)nx_ * ny_ * nz_, 0); auto icIdx = [&](u32 i, u32 j, u32 k) { return ((size_t)k * ny_ + j) * nx_ + i; }; auto satirDoldur = [&](u32 j) { for (u32 i = 0; i < nx_; ++i) { const std::vector& kol = set_[2].kolon[(size_t)j * set_[2].nu + i]; for (const Span& sp : kol) { const f64 o = dugum(2, 0); int k0 = (int)std::ceil(((f64)sp.t0 - 1e-6 - o) / h_); int k1 = (int)std::floor(((f64)sp.t1 + 1e-6 - o) / h_); k0 = std::max(0, k0); k1 = std::min((int)nz_ - 1, k1); for (int k = k0; k <= k1; ++k) ic[icIdx(i, j, (u32)k)] = 1; } } }; if (havuz_) havuz_->kos([&](unsigned j) { satirDoldur(j); }, ny_); else for (u32 j = 0; j < ny_; ++j) satirDoldur(j); // Kenar kesisimi: eksen d kenari (dugum n0→n1) icin ilgili dexel kolonundan // TAM konum + normal. Bulunamazsa (numerik uyusmazlik) orta nokta fallback. auto kesisim = [&](int d, u32 i, u32 j, u32 k, Vec3d& poz, Vec3d& nrm) -> void { int ue, ve; eksenUV(d, ue, ve); const u32 idx3[3] = {i, j, k}; const DexelSeti& s = set_[d]; const std::vector& kol = s.kolon[(size_t)idx3[ve] * s.nu + idx3[ue]]; const f64 wa = dugum(d, idx3[d]), wb = wa + h_; f64 w = (wa + wb) * 0.5; u32 np = 0; for (const Span& sp : kol) { if (sp.t0 > wa + 1e-7 && sp.t0 < wb - 1e-7) { w = sp.t0; np = sp.n0; break; } if (sp.t1 > wa + 1e-7 && sp.t1 < wb - 1e-7) { w = sp.t1; np = sp.n1; break; } } poz = Vec3d{dugum(0, i), dugum(1, j), dugum(2, k)}; (&poz.x)[d] = w; if (np != 0) nrm = octAc(np); else { nrm = Vec3d{0, 0, 0}; (&nrm.x)[d] = 1.0; } }; // Pass 1: karisik hucre → QEF vertex. PARALEL k-bant: her bant kendi hucre // araligina yazar (hucreV disjoint) + yerel vertex tamponu; sonra bant-SIRASINDA // ofset+birlestir → seri ile BIREBIR ayni siralama. std::vector hucreV((size_t)cx * cy * cz, -1); auto hIdx = [&](u32 i, u32 j, u32 k) { return ((size_t)k * cy + j) * cx + i; }; const unsigned T = havuz_ ? havuz_->isciSayisi() : 1; const u32 bantH = std::max(2, cz / std::max(1u, T * 3)); const u32 nBant = (cz + bantH - 1) / bantH; struct P1Bant { MeshVeri part; }; std::vector p1(nBant); auto p1Isle = [&](unsigned b) { MeshVeri& part = p1[b].part; const u32 kB0 = b * bantH, kB1 = std::min(cz, kB0 + bantH); for (u32 k = kB0; k < kB1; ++k) for (u32 j = 0; j < cy; ++j) for (u32 i = 0; i < cx; ++i) { u8 dur[8]; u32 kod = 0; for (int c = 0; c < 8; ++c) { dur[c] = ic[icIdx(i + mc::kKoseOfset[c][0], j + mc::kKoseOfset[c][1], k + mc::kKoseOfset[c][2])]; if (dur[c]) kod |= (1u << c); } if (kod == 0 || kod == 0xFF) continue; f64 A[6] = {0, 0, 0, 0, 0, 0}, bb[3] = {0, 0, 0}; Vec3d mass{0, 0, 0}, nTop{0, 0, 0}; int cnt = 0; for (int e = 0; e < 12; ++e) { const int a = mc::kKenarKose[e][0], bk = mc::kKenarKose[e][1]; if (dur[a] == dur[bk]) continue; const int dx = mc::kKoseOfset[bk][0] - mc::kKoseOfset[a][0]; const int dy = mc::kKoseOfset[bk][1] - mc::kKoseOfset[a][1]; const int dz = mc::kKoseOfset[bk][2] - mc::kKoseOfset[a][2]; const int d = (dx != 0) ? 0 : (dy != 0) ? 1 : 2; const int lo = (dx < 0 || dy < 0 || dz < 0) ? bk : a; // kucuk uc Vec3d pe, ne; kesisim(d, i + mc::kKoseOfset[lo][0], j + mc::kKoseOfset[lo][1], k + mc::kKoseOfset[lo][2], pe, ne); A[0] += ne.x * ne.x; A[1] += ne.x * ne.y; A[2] += ne.x * ne.z; A[3] += ne.y * ne.y; A[4] += ne.y * ne.z; A[5] += ne.z * ne.z; const f64 dp = ne.nokta(pe); bb[0] += ne.x * dp; bb[1] += ne.y * dp; bb[2] += ne.z * dp; mass += pe; nTop += ne; ++cnt; } if (cnt == 0) continue; mass = mass / (f64)cnt; const f64 lam = 0.05; A[0] += lam; A[3] += lam; A[5] += lam; bb[0] += lam * mass.x; bb[1] += lam * mass.y; bb[2] += lam * mass.z; f64 x[3]; Vec3d v = qefCoz(A, bb, x) ? Vec3d{x[0], x[1], x[2]} : mass; const Vec3d cmn{dugum(0, i), dugum(1, j), dugum(2, k)}; v.x = std::clamp(v.x, cmn.x, cmn.x + h_); v.y = std::clamp(v.y, cmn.y, cmn.y + h_); v.z = std::clamp(v.z, cmn.z, cmn.z + h_); const f64 nl = nTop.uzunlukKare(); const Vec3d nv = (nl < 1e-20) ? Vec3d{0, 0, 1} : nTop / std::sqrt(nl); hucreV[hIdx(i, j, k)] = (int)(part.pozisyon.size() / 3); // YEREL indeks part.pozisyon.push_back((f32)v.x); part.pozisyon.push_back((f32)v.y); part.pozisyon.push_back((f32)v.z); part.normal.push_back((f32)nv.x); part.normal.push_back((f32)nv.y); part.normal.push_back((f32)nv.z); part.sinir.genislet({(f32)v.x, (f32)v.y, (f32)v.z}); } }; if (havuz_ && nBant > 1) havuz_->kos(p1Isle, nBant); else for (unsigned b = 0; b < nBant; ++b) p1Isle(b); // Bant ofsetleri + hucreV yerel→global düzeltme (paralel; bantlar disjoint). std::vector ofset(nBant + 1, 0); for (u32 b = 0; b < nBant; ++b) ofset[b + 1] = ofset[b] + (u32)(p1[b].part.pozisyon.size() / 3); auto p1Duzelt = [&](unsigned b) { const int of = (int)ofset[b]; if (of == 0) return; const u32 kB0 = b * bantH, kB1 = std::min(cz, kB0 + bantH); for (size_t c = (size_t)kB0 * cy * cx; c < (size_t)kB1 * cy * cx; ++c) if (hucreV[c] >= 0) hucreV[c] += of; }; if (havuz_ && nBant > 1) havuz_->kos(p1Duzelt, nBant); else for (unsigned b = 0; b < nBant; ++b) p1Duzelt(b); cikti.pozisyon.reserve((size_t)ofset[nBant] * 3); cikti.normal.reserve((size_t)ofset[nBant] * 3); for (u32 b = 0; b < nBant; ++b) { MeshVeri& p = p1[b].part; cikti.pozisyon.insert(cikti.pozisyon.end(), p.pozisyon.begin(), p.pozisyon.end()); cikti.normal.insert(cikti.normal.end(), p.normal.begin(), p.normal.end()); if (p.sinir.gecerliMi()) { cikti.sinir.genislet(p.sinir.asgari); cikti.sinir.genislet(p.sinir.azami); } } p1.clear(); // Pass 2: kesilen primal kenar → 4 komsu hucre QUAD (sdf_motor DC deseni; sarim // T4 isaretli-hacim testiyle kilitli). PARALEL k-bant + yerel indeks tamponu → // bant-sirasinda birlestir (uc dongu k-dis → siralama seri ile ayni). auto quadEk = [&](std::vector& out, int a, int b, int c, int e, bool ters) { if (a < 0 || b < 0 || c < 0 || e < 0) return; if (ters) { out.push_back((u32)a); out.push_back((u32)b); out.push_back((u32)c); out.push_back((u32)a); out.push_back((u32)c); out.push_back((u32)e); } else { out.push_back((u32)a); out.push_back((u32)c); out.push_back((u32)b); out.push_back((u32)a); out.push_back((u32)e); out.push_back((u32)c); } }; auto V = [&](u32 i, u32 j, u32 k) { return hucreV[hIdx(i, j, k)]; }; // Uc kenar-yonu dongusu tek gorevde (bant k-araligi; X/Y kenarlari k>=1). std::vector> p2(nBant); auto p2Isle = [&](unsigned b) { std::vector& out = p2[b]; const u32 kB0 = b * bantH, kB1 = std::min(cz, kB0 + bantH); for (u32 k = std::max(1u, kB0); k < kB1; ++k) for (u32 j = 1; j < cy; ++j) for (u32 i = 0; i < cx; ++i) { const bool s0 = ic[icIdx(i, j, k)] != 0, s1 = ic[icIdx(i + 1, j, k)] != 0; if (s0 == s1) continue; quadEk(out, V(i, j - 1, k - 1), V(i, j, k - 1), V(i, j, k), V(i, j - 1, k), s0); } for (u32 k = std::max(1u, kB0); k < kB1; ++k) for (u32 j = 0; j < cy; ++j) for (u32 i = 1; i < cx; ++i) { const bool s0 = ic[icIdx(i, j, k)] != 0, s1 = ic[icIdx(i, j + 1, k)] != 0; if (s0 == s1) continue; quadEk(out, V(i - 1, j, k - 1), V(i, j, k - 1), V(i, j, k), V(i - 1, j, k), !s0); } for (u32 k = kB0; k < kB1; ++k) for (u32 j = 1; j < cy; ++j) for (u32 i = 1; i < cx; ++i) { const bool s0 = ic[icIdx(i, j, k)] != 0, s1 = ic[icIdx(i, j, k + 1)] != 0; if (s0 == s1) continue; quadEk(out, V(i - 1, j - 1, k), V(i, j - 1, k), V(i, j, k), V(i - 1, j, k), s0); } }; if (havuz_ && nBant > 1) havuz_->kos(p2Isle, nBant); else for (unsigned b = 0; b < nBant; ++b) p2Isle(b); size_t topI = 0; for (const auto& v : p2) topI += v.size(); cikti.indeks.reserve(topI); for (const auto& v : p2) cikti.indeks.insert(cikti.indeks.end(), v.begin(), v.end()); } // --------------------------------------------------------------------------- // Checkpoint — span verisi kompakt (O(yuzey)) → kopya ucuz // --------------------------------------------------------------------------- namespace { struct TdSnapshot { TriDexelMotoru::DexelSeti set[3]; f64 hacim = 0.0; }; } // namespace size_t TriDexelMotoru::durumBayt() const { size_t n = 0; for (const DexelSeti& s : set_) for (const auto& k : s.kolon) n += k.size() * sizeof(Span) + sizeof(void*) * 3; return n + sizeof(f64); } std::shared_ptr TriDexelMotoru::durumKaydet() const { if (!kuruldu_ || nx_ == 0) return nullptr; auto s = std::make_shared(); for (int d = 0; d < 3; ++d) s->set[d] = set_[d]; s->hacim = kaldirilanHacim_; return s; } bool TriDexelMotoru::durumYukle(const std::shared_ptr& s) { if (!s || !kuruldu_) return false; auto* snap = static_cast(s.get()); for (int d = 0; d < 3; ++d) { if (snap->set[d].nu != set_[d].nu || snap->set[d].nv != set_[d].nv) return false; set_[d] = snap->set[d]; } kaldirilanHacim_ = snap->hacim; return true; } } // namespace fiko::kutuk