#include "brep_ic.hpp" #include #include #include #include #include #include #include #include // yavaş alan hesabında çağıranı log'a yaz (yalnız teşhis dalında) #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ucgenle açık-kalite: bağımsız mesh kopyası #include #include #include #include #include #include #include #include #include #include #include #include // tanjantYuzler: kenar-yerel yüzey normali #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fiko/core/gunluk.hpp" #include "fiko/geometri/kalite.hpp" namespace fiko::geometri { using ic::ceviri; using ic::sar; using ic::sekil; BrepGovde::BrepGovde() = default; BrepGovde::BrepGovde(std::unique_ptr icerik) : icerik_(std::move(icerik)) {} BrepGovde::~BrepGovde() = default; BrepGovde::BrepGovde(BrepGovde&&) noexcept = default; BrepGovde& BrepGovde::operator=(BrepGovde&&) noexcept = default; bool BrepGovde::gecerliMi() const { return icerik_ && !icerik_->sekil.IsNull(); } void BrepGovde::yuzRenkleriAta(std::vector renkler) { if (icerik_) icerik_->yuzRenkleri = std::move(renkler); } bool BrepGovde::yuzRenkliMi() const { return icerik_ && !icerik_->yuzRenkleri.empty(); } const std::vector& BrepGovde::yuzRenkleri() const { static const std::vector bos; return icerik_ ? icerik_->yuzRenkleri : bos; } void ic::renkPropagasyonu(const BrepGovde& eski, BRepBuilderAPI_MakeShape& algo, BrepGovde& yeni) { if (!eski.yuzRenkliMi() || !yeni.gecerliMi()) return; const std::vector& eskiRenk = eski.yuzRenkleri(); const TopoDS_Shape& eskiSekil = sekil(eski); const TopoDS_Shape& yeniSekil = sekil(yeni); // Yeni yuzler ucgenle ile AYNI TopExp sirasinda (ordinal hizasi korunur). std::vector yeniYuzler; TopTools_IndexedMapOfShape yuzIdx; // yuz(IsSame) → 1-tabanli ordinal for (TopExp_Explorer k(yeniSekil, TopAbs_FACE); k.More(); k.Next()) { yeniYuzler.push_back(k.Current()); yuzIdx.Add(k.Current()); } if (yeniYuzler.empty()) return; // Onceki cagridan (boolean a→b) kalan renkleri koru; yoksa taban(-1) sentinel. const size_t n = yeniYuzler.size(); std::vector renk(n, Vec3f{0, 0, 0}); std::vector atandi(n, 0); if (yeni.yuzRenkliMi()) { const std::vector& onceki = yeni.yuzRenkleri(); for (size_t j = 0; j < n && j < onceki.size(); ++j) { renk[j] = onceki[j]; atandi[j] = 1; } } // Eski yuz → renk: degismeden gecen (IsSame) VEYA Modified() ile eslenen. u32 o = 0; for (TopExp_Explorer ek(eskiSekil, TopAbs_FACE); ek.More(); ek.Next()) { ++o; const TopoDS_Shape& ef = ek.Current(); const Vec3f c = (o <= eskiRenk.size()) ? eskiRenk[o - 1] : Vec3f{0, 0, 0}; if (o > eskiRenk.size()) continue; // kaynakta o yuz icin renk yok auto isaretle = [&](const TopoDS_Shape& yf) { if (yf.IsNull() || yf.ShapeType() != TopAbs_FACE) return; const int idx = yuzIdx.FindIndex(yf); // IsSame → 1-tabanli ordinal if (idx >= 1 && (size_t)idx <= n) { renk[idx - 1] = c; atandi[idx - 1] = 1; } }; if (!algo.IsDeleted(ef)) isaretle(ef); // degismeden gecen yuz const TopTools_ListOfShape& mods = algo.Modified(ef); // bolunmus/trimlenmis yeni yuzler for (TopTools_ListIteratorOfListOfShape it(mods); it.More(); it.Next()) isaretle(it.Value()); } // Atanmamis (yeni uretilen: pah/fillet yuzeyi) → kenar-komsu atanmis yuz rengi. bool herhangiAtanmis = false; for (char a : atandi) if (a) { herhangiAtanmis = true; break; } if (!herhangiAtanmis) return; // hicbir yuz eslesmedi → renk verme (tekduze kalir) TopTools_IndexedDataMapOfShapeListOfShape kenarYuz; TopExp::MapShapesAndAncestors(yeniSekil, TopAbs_EDGE, TopAbs_FACE, kenarYuz); // Düzlem yüz normali (orientation dahil) — coplanar komşu tercihinde kullanılır. const auto duzNormal = [](const TopoDS_Shape& f, gp_Dir& n) -> bool { try { BRepAdaptor_Surface sf(TopoDS::Face(f)); if (sf.GetType() != GeomAbs_Plane) return false; n = sf.Plane().Axis().Direction(); if (f.Orientation() == TopAbs_REVERSED) n.Reverse(); return true; } catch (const Standard_Failure&) { return false; } }; for (size_t j = 0; j < n; ++j) { if (atandi[j]) continue; // Atanmamış yeni yüz (uzatılan yan duvar / fillet). Komşu atanmış yüzlerden NORMALİ // EN YAKIN (coplanar) olanı seç → uzatılan yan duvar, uzatıldığı orijinal yüzle aynı // düzlemde olduğundan onun rengini alır (ötelenen üst yüzün değil). Kullanıcı isteği. gp_Dir nj; const bool njVar = duzNormal(yeniYuzler[j], nj); int enIyi = -1; f64 enSkor = -1.0; for (TopExp_Explorer ke(yeniYuzler[j], TopAbs_EDGE); ke.More(); ke.Next()) { if (!kenarYuz.Contains(ke.Current())) continue; const TopTools_ListOfShape& komsular = kenarYuz.FindFromKey(ke.Current()); for (TopTools_ListIteratorOfListOfShape it(komsular); it.More(); it.Next()) { const int idx = yuzIdx.FindIndex(it.Value()); if (idx < 1 || (size_t)idx > n || !atandi[idx - 1]) continue; f64 skor = 0.0; // coplanar değilse 0; ilk atanmış komşu yine de aday kalır gp_Dir nk; if (njVar && duzNormal(it.Value(), nk)) { skor = nj.Dot(nk); if (skor < 0) skor = -skor; } if (skor > enSkor) { enSkor = skor; enIyi = idx - 1; } } } if (enIyi >= 0) renk[j] = renk[enIyi]; else for (size_t k = 0; k < n; ++k) if (atandi[k]) { renk[j] = renk[k]; break; } } yeni.yuzRenkleriAta(std::move(renk)); } // AÇIK yüzeyde (kapalı olmayan shell/yüz seti — eksik yüz, onarılmamış VRML/STL importu // vb.) BRepGProp::VolumeProperties analitik integrali anlamlı sonuç veremez (~0 döner). // Web sürümündeki tetrahedra-toplamı ("signedVolumeOfTriangle") ile BİREBİR aynı mantık: // gövdenin RENDER üçgenlemesinden (ucgenle) her üçgen için orijine göre imzalı tetrahedron // hacmi toplanır (diverjans teoremi). Kapalı gövdede analitik sonuca (küçük tesellasyon // hatasıyla) yakın çıkar; açık gövdede KESİN değil ama YAKLAŞIK bir hacim üretir — sıfır/ // eksik kütleden iyi. Yalnız analitik yöntem başarısız olduğunda (aşağıda) devreye girer. static f64 meshTetrahedraHacmi(const MeshVeri& mesh) { f64 toplam = 0; for (size_t i = 0; i + 2 < mesh.indeksler.size(); i += 3) { const Vertex& a = mesh.vertexler[mesh.indeksler[i]]; const Vertex& b = mesh.vertexler[mesh.indeksler[i + 1]]; const Vertex& c = mesh.vertexler[mesh.indeksler[i + 2]]; toplam += (f64)a.konum[0] * ((f64)b.konum[1] * c.konum[2] - (f64)b.konum[2] * c.konum[1]) - (f64)a.konum[1] * ((f64)b.konum[0] * c.konum[2] - (f64)b.konum[2] * c.konum[0]) + (f64)a.konum[2] * ((f64)b.konum[0] * c.konum[1] - (f64)b.konum[1] * c.konum[0]); } return std::abs(toplam) / 6.0; } // Gövde hacmi/alanı BRepGProp integrali (dev modelde on ms'ler) ve ŞEKİL DEĞİŞMEDİKÇE // sabit — BrepGovde her işlemde YENİ Icerik ürettiği için memo güvenli (bayatlayamaz). // Hover kartı/kütle paneli aynı değeri tekrar tekrar soruyordu. // KRİTİK: pahalı BRepGProp integrali KİLİT DIŞINDA koşar. Kilidi tutarak hesaplamak, // aynı gövdeye dokunan BAŞKA thread'i (hover'ın yüz sorgusu) integral boyunca bloklar — // ölçüldü: hover karesi 12-13 s bekliyordu. Çift-kontrol: oku → hesapla → yaz. f64 BrepGovde::hacim() const { if (!gecerliMi()) return 0; { std::scoped_lock kilit(icerik_->yuzOnbKilit); if (icerik_->hacimHazir) return icerik_->hacimMemo; } GProp_GProps oz; // Eps'li (adaptif) overload: sabit dereceli Gauss integrasyonu uzun/ince (yüksek // en-boy oranlı) gövdelerde — özellikle silindirik/eğrisel yüzeylerde — büyük // bağıl hataya (nadiren işaret değişimine → negatif hacim) yol açabiliyor. // 1e-4 bağıl hata hedefiyle yineleyerek çok daha kararlı sonuç verir. BRepGProp::VolumeProperties(icerik_->sekil, oz, 1e-4); f64 v = oz.Mass(); if (std::abs(v) < 1e-6) { // Analitik integral hacim veremedi (büyük olasılıkla açık/kapalı-olmayan yüzey) → // mesh-tabanlı yaklaşık hacme düş (bkz. meshTetrahedraHacmi üstündeki not). const MeshVeri mesh = ucgenle({1.0f, 1.0f, 1.0f}, 0.0, false); if (mesh.indeksler.size() >= 3) v = meshTetrahedraHacmi(mesh); } std::scoped_lock kilit(icerik_->yuzOnbKilit); icerik_->hacimMemo = v; icerik_->hacimHazir = true; return v; } f64 BrepGovde::alan() const { if (!gecerliMi()) return 0; { std::scoped_lock kilit(icerik_->yuzOnbKilit); if (icerik_->alanHazir) return icerik_->alanMemo; } GProp_GProps oz; BRepGProp::SurfaceProperties(icerik_->sekil, oz); const f64 a = oz.Mass(); std::scoped_lock kilit(icerik_->yuzOnbKilit); icerik_->alanMemo = a; icerik_->alanHazir = true; return a; } u32 BrepGovde::yuzSayisi() const { if (!gecerliMi()) return 0; u32 n = 0; for (TopExp_Explorer e(icerik_->sekil, TopAbs_FACE); e.More(); e.Next()) ++n; return n; } KutleOzellik kutleOzellikleri(const BrepGovde& g, f64 yogunluk) { KutleOzellik k; if (!g.gecerliMi()) return k; GProp_GProps oz; // hacim() ile aynı gerekçe: eps'li adaptif integrasyon, uzun/ince gövdelerde // sabit dereceli integrasyonun neden olduğu negatif/hatalı hacmi önler. BRepGProp::VolumeProperties(sekil(g), oz, 1e-4); k.hacim = oz.Mass(); // birim yoğunlukla GProp "Mass" = hacim (mm³) if (std::abs(k.hacim) < 1e-6) { // hacim() ile aynı fallback: açık yüzeyde analitik hacim veremeyince mesh-tabanlı // yaklaşığa düş. NOT: CoG/asal atalet aşağıda hâlâ 'oz'dan geliyor — açık yüzeyde // bunlar tanımsız kalabilir (yalnız hacim/kütle sayısı düzeltiliyor). const MeshVeri mesh = g.ucgenle({1.0f, 1.0f, 1.0f}, 0.0, false); if (mesh.indeksler.size() >= 3) k.hacim = meshTetrahedraHacmi(mesh); } k.alan = g.alan(); k.kutle = k.hacim * yogunluk; const gp_Pnt c = oz.CentreOfMass(); k.agirlikMerkezi = {c.X(), c.Y(), c.Z()}; // Asal atalet: CoG etrafında asal momentler + eksenler (birim yoğunluk → kg·mm² için ×yogunluk). const GProp_PrincipalProps pp = oz.PrincipalProperties(); f64 i1 = 0, i2 = 0, i3 = 0; pp.Moments(i1, i2, i3); k.asalAtalet = {i1 * yogunluk, i2 * yogunluk, i3 * yogunluk}; const gp_Vec a1 = pp.FirstAxisOfInertia(), a2 = pp.SecondAxisOfInertia(), a3 = pp.ThirdAxisOfInertia(); k.asalEksen1 = {a1.X(), a1.Y(), a1.Z()}; k.asalEksen2 = {a2.X(), a2.Y(), a2.Z()}; k.asalEksen3 = {a3.X(), a3.Y(), a3.Z()}; k.gecerli = true; return k; } KarsilastirmaSonuc karsilastir(const BrepGovde& a, const BrepGovde& b, f64 tolerans) { KarsilastirmaSonuc s; if (!a.gecerliMi() || !b.gecerliMi()) return s; s.aHacim = a.hacim(); s.bHacim = b.hacim(); const BrepGovde aEksiB = cikarma(a, b); // A−B (A'da fazla) const BrepGovde bEksiA = cikarma(b, a); // B−A (B'de fazla) const BrepGovde ortakG = kesisim(a, b); // A∩B s.aFazla = aEksiB.gecerliMi() ? aEksiB.hacim() : 0.0; s.bFazla = bEksiA.gecerliMi() ? bEksiA.hacim() : 0.0; s.ortak = ortakG.gecerliMi() ? ortakG.hacim() : 0.0; // Özdeşlik: toplam fark hacmi / ortalama hacim < tolerans → modeller eşdeğer. const f64 ort = 0.5 * (s.aHacim + s.bHacim); s.ozdesMi = ort > 0.0 && (s.aFazla + s.bFazla) / ort < tolerans; s.gecerli = true; return s; } TopolojiOzet topolojiOzeti(const BrepGovde& g) { TopolojiOzet t; if (!g.gecerliMi()) return t; t.gecerliGovde = true; const TopoDS_Shape& s = ic::sekil(g); TopTools_IndexedMapOfShape harita; TopExp::MapShapes(s, TopAbs_VERTEX, harita); t.vertexSayi = harita.Extent(); harita.Clear(); TopExp::MapShapes(s, TopAbs_EDGE, harita); t.kenarSayi = harita.Extent(); harita.Clear(); TopExp::MapShapes(s, TopAbs_FACE, harita); t.yuzSayi = harita.Extent(); harita.Clear(); TopExp::MapShapes(s, TopAbs_SHELL, harita); t.kabukSayi = harita.Extent(); harita.Clear(); TopExp::MapShapes(s, TopAbs_SOLID, harita); t.katiSayi = harita.Extent(); try { t.gecerli = BRepCheck_Analyzer(s).IsValid(); } catch (const Standard_Failure&) { t.gecerli = false; } return t; } YuzTipOzet yuzTipleri(const BrepGovde& g) { YuzTipOzet t; if (!g.gecerliMi()) return t; for (TopExp_Explorer e(ic::sekil(g), TopAbs_FACE); e.More(); e.Next()) { BRepAdaptor_Surface yuzey(TopoDS::Face(e.Current())); switch (yuzey.GetType()) { case GeomAbs_Plane: ++t.duzlemsel; break; case GeomAbs_Cylinder: ++t.silindirik; break; case GeomAbs_Cone: ++t.konik; break; case GeomAbs_Sphere: ++t.kuresel; break; case GeomAbs_Torus: ++t.torus; break; default: ++t.digeri; break; // BSpline/Bezier/dönel/uzatma... } } return t; } Vec3d sinirBoyutu(const BrepGovde& g) { if (!g.gecerliMi()) return {0, 0, 0}; Bnd_Box kutucuk; BRepBndLib::Add(ic::sekil(g), kutucuk); if (kutucuk.IsVoid()) return {0, 0, 0}; f64 xmin, ymin, zmin, xmax, ymax, zmax; kutucuk.Get(xmin, ymin, zmin, xmax, ymax, zmax); return {xmax - xmin, ymax - ymin, zmax - zmin}; } SilindirSinir sinirSilindir(const BrepGovde& g, Vec3d eksenNokta, Vec3d eksenYon) { SilindirSinir r; if (!g.gecerliMi()) return r; const f64 dl = std::sqrt(eksenYon.nokta(eksenYon)); if (dl < 1e-12) return r; const Vec3d d = eksenYon * (1.0 / dl); // birim eksen yönü const MeshVeri m = g.ucgenle({0.5f, 0.5f, 0.5f}); if (m.vertexler.empty()) return r; f64 maxR = 0, minT = 1e300, maxT = -1e300; for (const auto& v : m.vertexler) { const Vec3d rel = Vec3d{v.konum[0], v.konum[1], v.konum[2]} - eksenNokta; const f64 t = rel.nokta(d); // eksen üzeri projeksiyon const Vec3d dik = rel - d * t; // eksene dik bileşen const f64 rr = std::sqrt(dik.nokta(dik)); if (rr > maxR) maxR = rr; if (t < minT) minT = t; if (t > maxT) maxT = t; } r.yaricap = maxR; r.uzunluk = maxT - minT; return r; } std::vector silindirYaricaplari(const BrepGovde& g) { std::vector sonuc; if (!g.gecerliMi()) return sonuc; for (TopExp_Explorer e(ic::sekil(g), TopAbs_FACE); e.More(); e.Next()) { BRepAdaptor_Surface yuzey(TopoDS::Face(e.Current())); if (yuzey.GetType() == GeomAbs_Cylinder) sonuc.push_back(yuzey.Cylinder().Radius()); } return sonuc; } f64 kenarUzunlugu(const BrepGovde& g) { if (!g.gecerliMi()) return 0.0; // BENZERSIZ kenarlar üzerinden topla: tüm-solid LinearProperties paylaşılan kenarı // yüz-başına çift sayar → TopExp::MapShapes ile her kenar bir kez. TopTools_IndexedMapOfShape harita; TopExp::MapShapes(ic::sekil(g), TopAbs_EDGE, harita); f64 toplam = 0.0; for (int i = 1; i <= harita.Extent(); ++i) { GProp_GProps oz; BRepGProp::LinearProperties(harita(i), oz); toplam += oz.Mass(); } return toplam; } f64 mesafe(const BrepGovde& a, const BrepGovde& b) { if (!a.gecerliMi() || !b.gecerliMi()) return -1.0; // geçersiz girdi try { BRepExtrema_DistShapeShape hesap(ic::sekil(a), ic::sekil(b)); if (!hesap.IsDone()) return -1.0; return hesap.Value(); // en yakın nokta çifti mesafesi (temas/çakışma → 0) } catch (const Standard_Failure&) { return -1.0; } } // Mesh-temelli gövde: en az bir yüz YÜZEYSIZ (Geom_Surface null) — VRML/STL ithal. // Bu yüzler zaten Poly_Triangulation taşır; BRepMesh/BRepCheck/Copy onlarda hem // YAVAŞ hem anlamsız (yeniden mesh edilemez, sağlık denetimi yüzey ister). Hızlı yol // için işaret (ilk yüz yeterli — VRML'de tüm yüzler yüzeysiz). O(1). static bool meshTemelliSekil(const TopoDS_Shape& s) { for (TopExp_Explorer k(s, TopAbs_FACE); k.More(); k.Next()) return BRep_Tool::Surface(TopoDS::Face(k.Current())).IsNull(); return false; } // Küçük simetrik lineer çözücü (n≤4, kısmi pivot Gauss) — Kåsa çember/küre fit için. static bool gaussCoz(int n, f64 A[4][5], f64* x) { for (int k = 0; k < n; ++k) { int piv = k; for (int i = k + 1; i < n; ++i) if (std::fabs(A[i][k]) > std::fabs(A[piv][k])) piv = i; if (std::fabs(A[piv][k]) < 1e-14) return false; if (piv != k) for (int j = k; j <= n; ++j) std::swap(A[piv][j], A[k][j]); for (int i = k + 1; i < n; ++i) { const f64 f = A[i][k] / A[k][k]; for (int j = k; j <= n; ++j) A[i][j] -= f * A[k][j]; } } for (int i = n - 1; i >= 0; --i) { f64 s = A[i][n]; for (int j = i + 1; j < n; ++j) s -= A[i][j] * x[j]; x[i] = s / A[i][i]; } return true; } // Simetrik 3x3'ün EN KÜÇÜK özdeğerli özvektörü (Jacobi süpürme) — silindir ekseni: // normal kovaryansı M=ΣA·u·uᵀ'de eksen, normallerin dik olduğu yön (λmin). static Vec3d enKucukOzvektor3(const f64 Min[3][3], f64& lmin) { f64 A[3][3] = {{Min[0][0], Min[0][1], Min[0][2]}, {Min[1][0], Min[1][1], Min[1][2]}, {Min[2][0], Min[2][1], Min[2][2]}}; f64 V[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; for (int s = 0; s < 24; ++s) { int p = 0, q = 1; f64 mx = std::fabs(A[0][1]); if (std::fabs(A[0][2]) > mx) { mx = std::fabs(A[0][2]); p = 0; q = 2; } if (std::fabs(A[1][2]) > mx) { mx = std::fabs(A[1][2]); p = 1; q = 2; } if (mx < 1e-14) break; const f64 theta = 0.5 * std::atan2(2 * A[p][q], A[q][q] - A[p][p]); const f64 c = std::cos(theta), sn = std::sin(theta); for (int i = 0; i < 3; ++i) { // A = Jᵀ A J (satır+sütun döndür) const f64 aip = A[i][p], aiq = A[i][q]; A[i][p] = c * aip - sn * aiq; A[i][q] = sn * aip + c * aiq; } for (int j = 0; j < 3; ++j) { const f64 apj = A[p][j], aqj = A[q][j]; A[p][j] = c * apj - sn * aqj; A[q][j] = sn * apj + c * aqj; } for (int i = 0; i < 3; ++i) { const f64 vip = V[i][p], viq = V[i][q]; V[i][p] = c * vip - sn * viq; V[i][q] = sn * vip + c * viq; } } int en = 0; if (A[1][1] < A[en][en]) en = 1; if (A[2][2] < A[en][en]) en = 2; lmin = A[en][en]; return Vec3d{V[0][en], V[1][en], V[2][en]}.birim(); } // ---- Yüzeysiz (mesh-temelli VRML/STL) yüz: triangulation'dan ANALİTİK TANIMA ---- // Hover kartı/ölçüm/benzerYuzler/geometrik çipler yuzBilgi'ye dayanır; yüzey yokken // düzlem→silindir→küre sırasıyla fit edilir (Kåsa). O(yüz düğümü) → hover'da µs // mertebesi, önbellek gerekmez. Uymayan yüz "serbest" kalır (alan/merkez/normal dolu). static bool meshYuzBilgi(const TopoDS_Face& yuz, YuzBilgi& bilgi) { TopLoc_Location konum; const Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(yuz, konum); if (tri.IsNull() || tri->NbTriangles() < 1) return false; const gp_Trsf don = konum.Transformation(); const bool ters = (yuz.Orientation() == TopAbs_REVERSED) != don.IsNegative(); const int nNod = tri->NbNodes(), nTri = tri->NbTriangles(); std::vector P((size_t)nNod); Aabb kutu; for (int i = 1; i <= nNod; ++i) { const gp_Pnt p = tri->Node(i).Transformed(don); P[(size_t)i - 1] = {p.X(), p.Y(), p.Z()}; kutu.genislet(P[(size_t)i - 1]); } const f64 kosegen = std::max(kutu.boyut().uzunluk(), 1e-9); f64 alanT = 0; Vec3d agirlik{}, bileske{}; f64 M[3][3] = {}; struct UcgenN { Vec3d u; Vec3d orta; f64 alan; }; std::vector un; un.reserve((size_t)nTri); for (int i = 1; i <= nTri; ++i) { int a, b, c; tri->Triangle(i).Get(a, b, c); if (ters) std::swap(b, c); const Vec3d& A = P[(size_t)a - 1]; const Vec3d& B = P[(size_t)b - 1]; const Vec3d& C = P[(size_t)c - 1]; const Vec3d n = (B - A).capraz(C - A); const f64 iki = n.uzunluk(); if (iki < 1e-14) continue; const f64 alan = iki * 0.5; const Vec3d u = n / iki; alanT += alan; agirlik = agirlik + (A + B + C) * (alan / 3.0); bileske = bileske + u * alan; for (int r = 0; r < 3; ++r) for (int cc = 0; cc < 3; ++cc) M[r][cc] += alan * (&u.x)[r] * (&u.x)[cc]; un.push_back({u, (A + B + C) / 3.0, alan}); } if (alanT <= 1e-12) return false; bilgi.alan = alanT; bilgi.merkez = agirlik / alanT; const Vec3d nOrt = (bileske.uzunlukKare() > 1e-24) ? bileske.birim() : Vec3d{0, 0, 1}; // 1) DÜZLEM: bileşke normal ~tam + düğümlerin düzlemden sapması küçük. if (bileske.uzunluk() / alanT >= 0.999) { f64 sap = 0; for (const Vec3d& p : P) sap = std::max(sap, std::fabs((p - bilgi.merkez).nokta(nOrt))); if (sap <= std::max(1e-6, 1e-3 * kosegen)) { bilgi.duzlemMi = true; bilgi.normal = nOrt; return true; } } // 2) SİLİNDİR: normaller eksene dik (kovaryans λmin küçük) + Kåsa çember fit. f64 lmin = 0; const Vec3d eksen = enKucukOzvektor3(M, lmin); if (lmin / alanT <= 5e-3) { // Eksene dik taban (e1,e2) kur, düğümleri 2B'ye izdüşür. Vec3d e1 = eksen.capraz(std::fabs(eksen.z) < 0.9 ? Vec3d{0, 0, 1} : Vec3d{1, 0, 0}).birim(); const Vec3d e2 = eksen.capraz(e1); f64 K[4][5] = {}; // artırılmış [A|b]: b sütunu K[i][3] (gaussCoz n=3) for (const Vec3d& p : P) { const Vec3d r = p - bilgi.merkez; const f64 x = r.nokta(e1), y = r.nokta(e2); const f64 rhs = x * x + y * y; const f64 v[3] = {2 * x, 2 * y, 1}; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) K[i][j] += v[i] * v[j]; K[i][3] += v[i] * rhs; } } f64 cz[3] = {}; if (gaussCoz(3, K, cz)) { const f64 R2 = cz[2] + cz[0] * cz[0] + cz[1] * cz[1]; if (R2 > 1e-12) { const f64 R = std::sqrt(R2); f64 sap = 0; for (const Vec3d& p : P) { const Vec3d r = p - bilgi.merkez; const f64 x = r.nokta(e1) - cz[0], y = r.nokta(e2) - cz[1]; sap = std::max(sap, std::fabs(std::sqrt(x * x + y * y) - R)); } if (sap <= std::max(2e-3 * R, 1e-3) && R <= 50.0 * kosegen) { bilgi.silindirMi = true; bilgi.yaricap = R; bilgi.eksen = eksen; bilgi.eksenNokta = bilgi.merkez + e1 * cz[0] + e2 * cz[1]; // Yüzey-üstü temsil noktası + radyal normal (işaret: gerçek üçgen // normalleriyle hizala — delik→içe, mil→dışa). const Vec3d rel = bilgi.merkez - bilgi.eksenNokta; const Vec3d rad = rel - eksen * rel.nokta(eksen); if (rad.uzunlukKare() > 1e-18) { Vec3d rb = rad.birim(); f64 isaret = 0; for (const UcgenN& t : un) { const Vec3d tr = t.orta - bilgi.eksenNokta; const Vec3d trad = tr - eksen * tr.nokta(eksen); if (trad.uzunlukKare() > 1e-18) isaret += t.alan * t.u.nokta(trad.birim()); } if (isaret < 0) rb = rb * -1.0; bilgi.normal = rb; bilgi.merkez = bilgi.eksenNokta + eksen * rel.nokta(eksen) + rb * R; } return true; } } } } // 3) KÜRE: Kåsa 3B fit (merkez+R), sapma küçükse küre. { f64 K[4][5] = {}; for (const Vec3d& p : P) { const Vec3d r = p - bilgi.merkez; const f64 v[4] = {2 * r.x, 2 * r.y, 2 * r.z, 1}; const f64 rhs = r.uzunlukKare(); for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) K[i][j] += v[i] * v[j]; K[i][4] += v[i] * rhs; } } f64 cz[4] = {}; if (gaussCoz(4, K, cz)) { const f64 R2 = cz[3] + cz[0] * cz[0] + cz[1] * cz[1] + cz[2] * cz[2]; if (R2 > 1e-12) { const f64 R = std::sqrt(R2); const Vec3d mrk = bilgi.merkez + Vec3d{cz[0], cz[1], cz[2]}; f64 sap = 0; for (const Vec3d& p : P) sap = std::max(sap, std::fabs((p - mrk).uzunluk() - R)); if (sap <= std::max(2e-3 * R, 1e-3) && R <= 50.0 * kosegen) { bilgi.kureMi = true; bilgi.yaricap = R; bilgi.eksenNokta = mrk; bilgi.normal = nOrt; return true; } } } } // 4) Serbest yüzey: tip yok ama alan/merkez/normal dolu (hover kartı yine bilgi verir). bilgi.normal = nOrt; return true; } // Üçgenlenmiş şekilden MeshVeri çıkar (ucgenle + ucgenleKaba ortak kuyruğu): yüz-başına // vertexler (keskin kenar), alan-ağırlıklı normal, winding = REVERSED XOR ayna-dönüşüm. static void meshCikar(const TopoDS_Shape& meshHedef, Vec3f renk, const std::vector& yuzRenkleri, MeshVeri& sonuc) { u32 yuzOrdinal = 0; for (TopExp_Explorer kesif(meshHedef, TopAbs_FACE); kesif.More(); kesif.Next()) { const TopoDS_Face yuz = TopoDS::Face(kesif.Current()); ++yuzOrdinal; // her yuz 1-tabanli ordinal alir (secim kimligi) // STEP yuz rengi varsa onu, yoksa tekduze govde rengini kullan. const Vec3f yuzRenk = (yuzOrdinal <= yuzRenkleri.size()) ? yuzRenkleri[yuzOrdinal - 1] : renk; TopLoc_Location konum; const Handle(Poly_Triangulation) ucgenler = BRep_Tool::Triangulation(yuz, konum); if (ucgenler.IsNull()) continue; const gp_Trsf donusum = konum.Transformation(); // Winding: yüz REVERSED ise takas + AYNA (negatif-determinant instance // dönüşümü, ör. STEP montajda aynalanmış parça) düğümleri aynaladığı için // winding'i ayrıca ters çevirir → XOR. Aksi halde ön yüzler back-cull'a // takılır (parça içi-dışına döner: "kamera tarafı saydam, arkası görünür"). const bool ters = (yuz.Orientation() == TopAbs_REVERSED) != donusum.IsNegative(); const u32 taban = (u32)sonuc.vertexler.size(); const int dugumSayi = ucgenler->NbNodes(); // Yuz basina vertexler ayri tutulur: dogal keskin kenarlar (CAD gorunumu) std::vector normaller((size_t)dugumSayi, Vec3f{}); for (int i = 1; i <= ucgenler->NbTriangles(); ++i) { int a, b, c; ucgenler->Triangle(i).Get(a, b, c); if (ters) std::swap(b, c); const gp_Pnt pa = ucgenler->Node(a).Transformed(donusum); const gp_Pnt pb = ucgenler->Node(b).Transformed(donusum); const gp_Pnt pc = ucgenler->Node(c).Transformed(donusum); const Vec3f va{(f32)pa.X(), (f32)pa.Y(), (f32)pa.Z()}; const Vec3f vb{(f32)pb.X(), (f32)pb.Y(), (f32)pb.Z()}; const Vec3f vc{(f32)pc.X(), (f32)pc.Y(), (f32)pc.Z()}; const Vec3f n = (vb - va).capraz(vc - va); // alan agirlikli normaller[(size_t)a - 1] += n; normaller[(size_t)b - 1] += n; normaller[(size_t)c - 1] += n; sonuc.indeksler.push_back(taban + (u32)a - 1); sonuc.indeksler.push_back(taban + (u32)b - 1); sonuc.indeksler.push_back(taban + (u32)c - 1); } for (int i = 1; i <= dugumSayi; ++i) { const gp_Pnt p = ucgenler->Node(i).Transformed(donusum); const Vec3f n = normaller[(size_t)i - 1].birim(); sonuc.vertexler.push_back({{(f32)p.X(), (f32)p.Y(), (f32)p.Z()}, {n.x, n.y, n.z}, {yuzRenk.x, yuzRenk.y, yuzRenk.z}, yuzOrdinal}); sonuc.sinir.genislet({(f32)p.X(), (f32)p.Y(), (f32)p.Z()}); } } sonuc.yuzSayi = yuzOrdinal; } bool BrepGovde::doluMu() const { // UCUZ doluluk testi: ≥1 solid + boş-olmayan bbox. Doğrulama KAPILARINDA hacim()sekil, TopAbs_SOLID); se.More(); se.Next()) { solidVar = true; break; } if (!solidVar) return false; Bnd_Box kutu; BRepBndLib::Add(icerik_->sekil, kutu); return !kutu.IsVoid() && kutu.SquareExtent() > 1e-12; } MeshVeri BrepGovde::ucgenle(Vec3f renk, f64 kaliteOrani, bool paralel) const { MeshVeri sonuc; if (!gecerliMi()) return sonuc; try { Bnd_Box kutucuk; BRepBndLib::Add(icerik_->sekil, kutucuk); const f64 kosegen = kutucuk.IsVoid() ? 1.0 : std::sqrt(kutucuk.SquareExtent()); // kaliteOrani<=0 → merkezi kalite() (runtime seviye); >0 → cagirandan gelen // acik oran (STL/OBJ export vb.). Acisal sapma daima merkezi seviyeden. const f64 oran = (kaliteOrani > 0.0) ? kaliteOrani : kalite().linOran; f64 sapma = std::max(kosegen * oran, 1e-4); // KUANTALA (1.25 kademeleri): feature işlemi bbox'ı mikro-değiştirince sapma da %0.x // oynayıp IncrementalMesh'in yüz önbelleğini TOPTAN geçersiz kılıyordu (devasa modelde // her fillet/it-çek = TAM remesh). Kademe değişmedikçe yalnız YENİ yüzler mesh'lenir. if (sapmaKuantala()) sapma = std::pow(1.25, std::round(std::log(sapma) / std::log(1.25))); // Açık kalite isteği (kaba LOD / export): BAĞIMSIZ KOPYA üzerinde mesh'le. // IncrementalMesh var olan DAHA İNCE üçgenlemeyi "yeterli" sayıp atlar → aynı // shape'te ince'den sonra kaba istemek sessizce İNCE meshin kopyasını dönerdi // (orbit LOD hiç kabalaşmaz, dev modelde kasma) ve ince istek kaba shape'i // yeniden mesh'leyip önbelleği bozardı. copyGeom=false: yüzey/eğri geometrisi // PAYLAŞILIR (ucuz, salt-okunur), TShape'ler kopyalanır → üçgenleme yuvası // bağımsız; yüz sırası (ordinal) yapı birebir korunduğundan aynıdır. TopoDS_Shape meshHedef = icerik_->sekil; if (kaliteOrani > 0.0) { BRepBuilderAPI_Copy kopya(icerik_->sekil, /*copyGeom*/ Standard_False, /*copyMesh*/ Standard_False); if (!kopya.Shape().IsNull()) meshHedef = kopya.Shape(); } // Açısal sapma: açık-kalite isteğinde LINEER oranla birlikte ölçeklenir — eğri // yüzeylerde üçgen sayısını çoğu zaman AÇISAL sınır belirler; sabit kalsaydı // "kaba" istek (orbit LOD, 10× lineer) gerçekte ~hiç kabalaşmazdı. f64 angRad = kalite().yuzAngRad; if (kaliteOrani > 0.0 && kalite().linOran > 0.0) angRad = std::clamp(angRad * (kaliteOrani / kalite().linOran), 0.05, 1.2); // isInParallel=false: OCCT OSD_ThreadPool'u KULLANMA. Boolean (cut/fuse) sonrası mesh // paralel çalışınca nested thread-pool → std::system_error "resource deadlock". Seri güvenli. // Faz ölçümü ("önce ölç"): önceden-üçgenli yüz sayısı + mesh vs çıkarım süresi — // incremental'ın gerçekten çalışıp çalışmadığını kanıtlar (>100ms'te loglanır). u32 onceUcgenli = 0, toplamYuz = 0; for (TopExp_Explorer k(meshHedef, TopAbs_FACE); k.More(); k.Next()) { ++toplamYuz; TopLoc_Location l; if (!BRep_Tool::Triangulation(TopoDS::Face(k.Current()), l).IsNull()) ++onceUcgenli; } const auto um0 = std::chrono::steady_clock::now(); // Mesh-temelli (yüzeysiz VRML/STL) gövdede yüzler zaten üçgenli → BRepMesh ATLA // (yeniden mesh edemez, yalnız 4000+ yüzü boşuna gezer = kasma). meshCikar mevcut // Poly_Triangulation'ı okur. Gerçek B-rep (yüzeyli) her zamanki gibi mesh'lenir. if (!meshTemelliSekil(meshHedef)) BRepMesh_IncrementalMesh(meshHedef, sapma, false, angRad, paralel); const auto um1 = std::chrono::steady_clock::now(); meshCikar(meshHedef, renk, icerik_->yuzRenkleri, sonuc); const auto um2 = std::chrono::steady_clock::now(); const f64 meshMs = std::chrono::duration(um1 - um0).count(); if (meshMs + std::chrono::duration(um2 - um1).count() > 100.0) gunluk::bilgi("[perf] ucgenle: mesh {:.0f}ms (önceden üçgenli {}/{} yüz, sapma {:.4f}) + " "çıkarım {:.0f}ms", meshMs, onceUcgenli, toplamYuz, sapma, std::chrono::duration(um2 - um1).count()); } catch (const Standard_Failure& hata) { gunluk::hata("Ucgenleme hatasi: {}", hata.what()); } return sonuc; } MeshVeri BrepGovde::ucgenleKaba(Vec3f renk, const KabaOnbellek* eski, std::shared_ptr& yeni) const { MeshVeri sonuc; yeni.reset(); if (!gecerliMi()) return sonuc; // Mesh-temelli (yüzeysiz VRML/STL): kaba LOD yeniden mesh EDİLEMEZ (yüzey yok) → // Copy+BRepMesh yalnız 4000+ yüzü boşuna gezip aynı meshi 15+ sn'de üretir (donma). // İnce mesh'i doğrudan dön (kaba=ince; kabul edilebilir — VRML zaten sabit tessellation). if (meshTemelliSekil(icerik_->sekil)) return ucgenle(renk); try { Bnd_Box kutucuk; BRepBndLib::Add(icerik_->sekil, kutucuk); const f64 kosegen = kutucuk.IsVoid() ? 1.0 : std::sqrt(kutucuk.SquareExtent()); f64 sapma = std::max(kosegen * kabaLodSapma(), 1e-4); if (sapmaKuantala()) sapma = std::pow(1.25, std::round(std::log(sapma) / std::log(1.25))); f64 angRad = kalite().yuzAngRad; if (kalite().linOran > 0.0) angRad = std::clamp(angRad * (kabaLodSapma() / kalite().linOran), 0.05, 1.2); // Bağımsız kopyada mesh'le (ucgenle açık-kalite gerekçesi: ince/kaba önbellek çakışmaz). BRepBuilderAPI_Copy kopyala(icerik_->sekil, /*copyGeom*/ Standard_False, /*copyMesh*/ Standard_False); TopoDS_Shape kopya = kopyala.Shape(); if (kopya.IsNull()) return ucgenle(renk, kabaLodSapma()); // dejenere → eski yol // DEĞİŞMEYEN yüzlerin kaba üçgenlemesi ESKİ kopyadan TRANSFER edilir (Copy yüz sırasını // korur: kaynak i ↔ kopya i; eski-kaynak ↔ bu-kaynak IsSame). Böylece feature sonrası // kaba LOD da yalnız değişen yüzleri üçgenler (paylaşılan Poly_Triangulation salt-okunur; // IncrementalMesh gerekirse handle'ı DEĞİŞTİRİR, yerinde bozmaz — eski kopya etkilenmez). if (eski && eski->sapma == sapma && !eski->kaynak.IsNull() && !eski->kopya.IsNull()) { TopTools_IndexedMapOfShape eskiIdx; std::vector eskiKopyaYuz; TopExp_Explorer ek(eski->kaynak, TopAbs_FACE), kk(eski->kopya, TopAbs_FACE); for (; ek.More() && kk.More(); ek.Next(), kk.Next()) { const int once = eskiIdx.Extent(); if (eskiIdx.Add(ek.Current()) > once) eskiKopyaYuz.push_back(TopoDS::Face(kk.Current())); } BRep_Builder kur; TopExp_Explorer yk(icerik_->sekil, TopAbs_FACE), kp(kopya, TopAbs_FACE); for (; yk.More() && kp.More(); yk.Next(), kp.Next()) { const int idx = eskiIdx.FindIndex(yk.Current()); if (idx < 1 || (size_t)idx > eskiKopyaYuz.size()) continue; TopLoc_Location konum; const Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(eskiKopyaYuz[(size_t)idx - 1], konum); if (!tri.IsNull()) kur.UpdateFace(TopoDS::Face(kp.Current()), tri); } } BRepMesh_IncrementalMesh(kopya, sapma, false, angRad, false); meshCikar(kopya, renk, icerik_->yuzRenkleri, sonuc); auto onb = std::make_shared(); onb->kaynak = icerik_->sekil; onb->kopya = kopya; onb->sapma = sapma; yeni = std::move(onb); } catch (const Standard_Failure& hata) { gunluk::hata("Kaba mesh hatasi: {}", hata.what()); } return sonuc; } KenarVeri BrepGovde::kenarMesh(f64 kaliteOrani, const BrepGovde* eskiKaynak) const { KenarVeri sonuc; if (!gecerliMi()) return sonuc; try { Bnd_Box kutucuk; BRepBndLib::Add(icerik_->sekil, kutucuk); const f64 kosegen = kutucuk.IsVoid() ? 1.0 : std::sqrt(kutucuk.SquareExtent()); const f64 oran = (kaliteOrani > 0.0) ? kaliteOrani : kalite().linOran; f64 sapma = std::max(kosegen * oran, 1e-4); // Sapma kademeleme (ucgenle ile aynı gerekçe): bbox mikro-değişse de sapma sabit kalır → // eski gövdenin kenar önbelleği geçerli, yalnız DEĞİŞEN kenarlar yeniden örneklenir. if (sapmaKuantala()) sapma = std::pow(1.25, std::round(std::log(sapma) / std::log(1.25))); // Eski gövde önbelleği uygunsa IsSame haritası kur (TShape+Location birebir — değişmeden // geçen kenar aynı polyline'ı üretir; IndexedMap paylaşılan kenarı dedup ettiğinden map // indeksi→İLK ordinal ayrıca tutulur). const Icerik* eskiIc = nullptr; TopTools_IndexedMapOfShape eskiIdx; std::vector eskiMapOrd; if (eskiKaynak && eskiKaynak->gecerliMi() && eskiKaynak->icerik_ && eskiKaynak->icerik_->kenarOnbSapma == sapma && !eskiKaynak->icerik_->kenarOnbAralik.empty()) { eskiIc = eskiKaynak->icerik_.get(); u32 o = 0; for (TopExp_Explorer k(eskiIc->sekil, TopAbs_EDGE); k.More(); k.Next()) { ++o; const int once = eskiIdx.Extent(); if (eskiIdx.Add(k.Current()) > once) eskiMapOrd.push_back(o); } } // Kendi önbelleğimizi bu geçişte doldur (bir SONRAKİ işlem bizden kopyalasın). icerik_->kenarOnbSapma = sapma; icerik_->kenarOnbAralik.clear(); icerik_->kenarOnbNokta.clear(); const auto polyEkle = [&](u32 ordinal, const Vec3f* nokta, u32 n) { icerik_->kenarOnbAralik.push_back({(u32)icerik_->kenarOnbNokta.size(), n}); icerik_->kenarOnbNokta.insert(icerik_->kenarOnbNokta.end(), nokta, nokta + n); for (u32 i = 1; i < n; ++i) { sonuc.vertexler.push_back({{nokta[i - 1].x, nokta[i - 1].y, nokta[i - 1].z}, ordinal}); sonuc.vertexler.push_back({{nokta[i].x, nokta[i].y, nokta[i].z}, ordinal}); } }; u32 kenarOrdinal = 0; std::vector tampon; for (TopExp_Explorer kesif(icerik_->sekil, TopAbs_EDGE); kesif.More(); kesif.Next()) { const TopoDS_Edge kenar = TopoDS::Edge(kesif.Current()); ++kenarOrdinal; // 1) Değişmeden geçen kenar → eski havuzdan kopyala (örnekleme YOK). if (eskiIc) { const int idx = eskiIdx.FindIndex(kenar); if (idx >= 1 && (size_t)idx <= eskiMapOrd.size()) { const u32 eo = eskiMapOrd[(size_t)idx - 1]; if (eo >= 1 && eo <= eskiIc->kenarOnbAralik.size()) { const auto [bas, n] = eskiIc->kenarOnbAralik[eo - 1]; if ((size_t)bas + n <= eskiIc->kenarOnbNokta.size()) { polyEkle(kenarOrdinal, eskiIc->kenarOnbNokta.data() + bas, n); continue; } } } } // 2) Yeni/değişen kenar → eğri örnekle (eski davranış). BRepAdaptor_Curve uyarli(kenar); GCPnts_TangentialDeflection ornekleyici(uyarli, sapma, kalite().kenarAngRad, 2); const int n = ornekleyici.NbPoints(); if (n < 2) { icerik_->kenarOnbAralik.push_back({(u32)icerik_->kenarOnbNokta.size(), 0}); continue; } tampon.clear(); tampon.reserve((size_t)n); for (int i = 1; i <= n; ++i) { const gp_Pnt p = ornekleyici.Value(i); tampon.push_back({(f32)p.X(), (f32)p.Y(), (f32)p.Z()}); } polyEkle(kenarOrdinal, tampon.data(), (u32)tampon.size()); } sonuc.kenarSayi = kenarOrdinal; } catch (const Standard_Failure& hata) { gunluk::hata("Kenar cikarma hatasi: {}", hata.what()); } return sonuc; } namespace { // Ordinaldeki alt-sekli bul (ucgenle/kenarMesh/noktaMesh ile ayni TopExp sirasi) bool ordinalBul(const TopoDS_Shape& govde, TopAbs_ShapeEnum tip, u32 ordinal, TopoDS_Shape& cikti) { if (ordinal == 0) return false; u32 i = 0; for (TopExp_Explorer k(govde, tip); k.More(); k.Next()) if (++i == ordinal) { cikti = k.Current(); return true; } return false; } // O(1) ordinal→shape: Icerik cache'i lazy kurar (TopExp sırasıyla birebir), sonra // doğrudan indeksler. Her seçim/pick/bilgi çağrısı O(n) tarama yerine O(1) olur — // milyon-yüzlü modelde seçim yolunun O(n²) darboğazını kaldırır. const TopoDS_Shape* ordinalCache(const BrepGovde::Icerik& ic, TopAbs_ShapeEnum tip, u32 ordinal) { if (ordinal == 0) return nullptr; std::vector& c = (tip == TopAbs_FACE) ? ic.yuzOrd : (tip == TopAbs_EDGE) ? ic.kenarOrd : ic.vertexOrd; if (c.empty()) for (TopExp_Explorer k(ic.sekil, tip); k.More(); k.Next()) c.push_back(k.Current()); if (ordinal > c.size()) return nullptr; return &c[ordinal - 1]; } bool ordinalBulC(const BrepGovde::Icerik& ic, TopAbs_ShapeEnum tip, u32 ordinal, TopoDS_Shape& cikti) { const TopoDS_Shape* s = ordinalCache(ic, tip, ordinal); if (!s) return false; cikti = *s; return true; } } static u32 ileriEsleIc(const BrepGovde::Icerik& eskiIc, const BrepGovde::Icerik& yeniIc, TopAbs_ShapeEnum tip, u32 ord) { TopoDS_Shape s; if (!ordinalBulC(eskiIc, tip, ord, s)) return 0; u32 o = 0; for (TopExp_Explorer k(yeniIc.sekil, tip); k.More(); k.Next()) { ++o; if (k.Current().IsSame(s)) return o; } return 0; } u32 BrepGovde::yuzIleriEsle(const BrepGovde& yeni, u32 ord) const { if (!gecerliMi() || !yeni.gecerliMi() || ord == 0) return 0; return ileriEsleIc(*icerik_, *yeni.icerik_, TopAbs_FACE, ord); } u32 BrepGovde::kenarIleriEsle(const BrepGovde& yeni, u32 ord) const { if (!gecerliMi() || !yeni.gecerliMi() || ord == 0) return 0; return ileriEsleIc(*icerik_, *yeni.icerik_, TopAbs_EDGE, ord); } // ---- Kalıcı Yüz Adı (KYA) — u64 kalıcı etiket (ordinalCache ile BİREBİR TopExp_Explorer sırası) ---- namespace { u32 topExpSay(const TopoDS_Shape& s, TopAbs_ShapeEnum tip) { u32 n = 0; for (TopExp_Explorer k(s, tip); k.More(); k.Next()) ++n; return n; } u64 kidAlIc(const std::vector& v, u32 ord) { return (ord && ord <= v.size()) ? v[ord - 1] : 0; } u32 kidOrdIc(const std::vector& v, u64 kid) { if (!kid) return 0; for (std::size_t i = 0; i < v.size(); ++i) if (v[i] == kid) return (u32)(i + 1); return 0; } // Yüz shape → ordinal (TopExp sırası) O(1) arama: TShape-ptr bucket + IsSame. Lineer O(F) IsSame // (yüz başına, kenar/nokta propagasyonda O(E·F)/O(V·F) → devasa modelde saniyeler) yerine O(E)/O(V). // TAM-IsSame semantiği (TShape+Location; bucket içinde IsSame doğrular) → paylaşılan-TShape/pattern GÜVENLİ. struct YuzOrdBul { std::unordered_map>> h; explicit YuzOrdBul(const TopoDS_Shape& s) { u32 o = 0; for (TopExp_Explorer k(s, TopAbs_FACE); k.More(); k.Next()) h[k.Current().TShape().get()].push_back({k.Current(), ++o}); } u32 operator()(const TopoDS_Shape& f) const { const auto it = h.find(f.TShape().get()); if (it != h.end()) for (const auto& [sh, o] : it->second) if (sh.IsSame(f)) return o; // bucket genelde tek eleman → O(1) return 0; } }; // Her KENAR ordinali (TopExp sırası) için bitişik yüz KID çifti (sıralı {min,max}; topolojik // kenar imzası). Ancestors haritası + yüz listesi BİR KEZ kurulur → kenar başına kenarKomsuYuzler // (her çağrıda MapShapesAndAncestors yeniden kurardı, O(E·(E+F))) yerine tek kurulum + IsSame. std::vector> kenarYuzKidCift(const TopoDS_Shape& s, const std::function& yuzKid) { const YuzOrdBul ordBul(s); // O(1) yüz→ordinal (lineer O(F) IsSame yerine) TopTools_IndexedDataMapOfShapeListOfShape ef; TopExp::MapShapesAndAncestors(s, TopAbs_EDGE, TopAbs_FACE, ef); std::vector> cift; for (TopExp_Explorer e(s, TopAbs_EDGE); e.More(); e.Next()) { u64 a = 0, b = 0; int bulunan = 0; if (ef.Contains(e.Current())) for (const TopoDS_Shape& f : ef.FindFromKey(e.Current())) { const u32 od = ordBul(f); if (!od) continue; const u64 k = yuzKid(od); if (bulunan == 0) { a = k; bulunan = 1; } else { b = k; break; } } if (a > b) std::swap(a, b); cift.push_back({a, b}); } return cift; } // Her VERTEX ordinali (TopExp sırası) için bitişik YÜZ KID'lerinin SIRALI-tekil kümesi // (topolojik nokta imzası). Köşe ötelense de bitişik yüzler kalıcı → küme aynı = stabil kimlik. std::vector> noktaYuzKidImza(const TopoDS_Shape& s, const std::function& yuzKid) { const YuzOrdBul ordBul(s); // O(1) yüz→ordinal (lineer O(F) IsSame yerine → O(V·F)→O(V)) TopTools_IndexedDataMapOfShapeListOfShape vf; TopExp::MapShapesAndAncestors(s, TopAbs_VERTEX, TopAbs_FACE, vf); std::vector> sig; for (TopExp_Explorer v(s, TopAbs_VERTEX); v.More(); v.Next()) { std::vector kids; if (vf.Contains(v.Current())) for (const TopoDS_Shape& f : vf.FindFromKey(v.Current())) if (const u32 od = ordBul(f)) { if (const u64 k = yuzKid(od)) kids.push_back(k); } std::sort(kids.begin(), kids.end()); kids.erase(std::unique(kids.begin(), kids.end()), kids.end()); sig.push_back(std::move(kids)); } return sig; } } // namespace void BrepGovde::kimlikleriBaslat() { if (!gecerliMi()) return; auto& ic = *icerik_; const auto ata = [&](TopAbs_ShapeEnum tip, std::vector& kid) { const u32 n = topExpSay(ic.sekil, tip); if (kid.size() < n) kid.resize(n, 0); // yeni doğan ordinaller (atanmamış = 0) else if (kid.size() > n) kid.resize(n); // topoloji küçüldü → fazlalık etiket düşer for (u32 i = 0; i < n; ++i) if (kid[i] == 0) kid[i] = kaliciKimlikSonraki(); // atanmamışa GLOBAL-benzersiz etiket }; ata(TopAbs_FACE, ic.yuzKid); ata(TopAbs_EDGE, ic.kenarKid); ata(TopAbs_VERTEX, ic.vertexKid); } bool BrepGovde::kimlikliMi() const { return icerik_ && (!icerik_->yuzKid.empty() || !icerik_->kenarKid.empty() || !icerik_->vertexKid.empty()); } u64 BrepGovde::yuzKimlik(u32 ordinal) const { return icerik_ ? kidAlIc(icerik_->yuzKid, ordinal) : 0; } u64 BrepGovde::kenarKimlik(u32 ordinal) const { return icerik_ ? kidAlIc(icerik_->kenarKid, ordinal) : 0; } u64 BrepGovde::noktaKimlik(u32 ordinal) const { return icerik_ ? kidAlIc(icerik_->vertexKid, ordinal) : 0; } u32 BrepGovde::yuzKimlikOrd(u64 kid) const { return icerik_ ? kidOrdIc(icerik_->yuzKid, kid) : 0; } u32 BrepGovde::kenarKimlikOrd(u64 kid) const { return icerik_ ? kidOrdIc(icerik_->kenarKid, kid) : 0; } u32 BrepGovde::noktaKimlikOrd(u64 kid) const { return icerik_ ? kidOrdIc(icerik_->vertexKid, kid) : 0; } const std::vector& BrepGovde::yuzKimlikleri() const { static const std::vector bos; return icerik_ ? icerik_->yuzKid : bos; } const std::vector& BrepGovde::kenarKimlikleri() const { static const std::vector bos; return icerik_ ? icerik_->kenarKid : bos; } const std::vector& BrepGovde::noktaKimlikleri() const { static const std::vector bos; return icerik_ ? icerik_->vertexKid : bos; } void BrepGovde::kimlikleriYukle(std::vector yuz, std::vector kenar, std::vector nokta) { if (!icerik_) return; icerik_->yuzKid = std::move(yuz); icerik_->kenarKid = std::move(kenar); icerik_->vertexKid = std::move(nokta); // Global sayacı yüklenen en büyük etiketin üstüne çek → yeni etiketler eskilerle çakışmasın. u64 enBuyuk = 0; for (const auto* v : {&icerik_->yuzKid, &icerik_->kenarKid, &icerik_->vertexKid}) for (u64 k : *v) if (k > enBuyuk) enBuyuk = k; if (enBuyuk) kaliciKimlikSayacEnAz(enBuyuk); } void BrepGovde::kimlikMiras(const BrepGovde& girdi, const std::vector& yuzGeri) { if (!gecerliMi()) return; auto& ic = *icerik_; // GİRDİ GEÇERSİZ (miras alacak bir öncül YOK): zincirin İLK feature'ı — sketch'ten // ilk Pad/Shaft, boş belgede. Bu durumda miras yoktur, her öğe TAZE etiket alır. // Koruma olmadan aşağıdaki `girdi.icerik_->sekil` NULL kabı dereferans ediyordu: // ölçüldü 07-21, DM'de sketch→Pad sürüklemesi ve makro/MCP Pad'i tam burada // düşüyordu ("resource deadlock would occur"; teşhis adımı PRV:kimlikMiras). if (!girdi.gecerliMi() || !girdi.icerik_) { const auto tazele = [](std::vector& kid, u32 n) { kid.assign(n, 0); for (u32 i = 0; i < n; ++i) kid[i] = kaliciKimlikSonraki(); }; tazele(ic.yuzKid, topExpSay(ic.sekil, TopAbs_FACE)); tazele(ic.kenarKid, topExpSay(ic.sekil, TopAbs_EDGE)); tazele(ic.vertexKid, topExpSay(ic.sekil, TopAbs_VERTEX)); return; } // --- YÜZ (KESİN: history+geometrik geri-harita) --- const u32 ny = topExpSay(ic.sekil, TopAbs_FACE); ic.yuzKid.assign(ny, 0); std::unordered_set kullanilanY; for (u32 j = 0; j < ny; ++j) { const int g = (j < yuzGeri.size()) ? yuzGeri[j] : 0; if (g > 0) { const u64 k = girdi.yuzKimlik((u32)g); if (k && kullanilanY.insert(k).second) ic.yuzKid[j] = k; // miras (aynı yüz = aynı etiket) } } for (u32 j = 0; j < ny; ++j) if (ic.yuzKid[j] == 0) ic.yuzKid[j] = kaliciKimlikSonraki(); // op yarattı / bölünme → taze constexpr f64 EPS = 1e-7; // --- KENAR: TOPOLOJİK kimlik (bitişik yüz KID çifti) + geometrik tiebreak/fallback --- // Kenar = iki yüzün buluşması → {yüzKID_A, yüzKID_B} çifti ile kimliklenir. Yüzler kalıcı // olduğundan kenar ÖTELENSE/trimlense de aynı çift → AYNI etiket (saf geometriden çok stabil; // CAD standart edge-naming tekniği). Çift tekrarı (aynı 2 yüz arası >1 kenar) geometrik en-yakınla // ayrılır. Çift 0-0 (serbest kenar) veya eşleşmezse geometrik eşleme; o da yoksa taze. // Kenar imzaları: bitişik yüz KID çiftleri BİR KEZ (harita tek kurulum) — girdi + sonuç. const auto gCift = kenarYuzKidCift(girdi.icerik_->sekil, [&](u32 f) { return girdi.yuzKimlik(f); }); const auto mCift = kenarYuzKidCift(ic.sekil, [&](u32 f) { return yuzKimlik(f); }); struct GKenarImza { std::pair cift; u64 kid; Vec3d orta; f64 uz; }; std::vector gk; for (u32 o = 1;; ++o) { KenarBilgi kb; if (!girdi.kenarBilgi(o, kb)) break; const std::pair c = (o - 1 < gCift.size()) ? gCift[o - 1] : std::pair{0, 0}; gk.push_back({c, girdi.kenarKimlik(o), kb.orta, kb.uzunluk}); } std::vector gNokta; for (u32 o = 1;; ++o) { Vec3d p; if (!girdi.noktaKonum(o, p)) break; gNokta.push_back(p); } const u32 nk = topExpSay(ic.sekil, TopAbs_EDGE); ic.kenarKid.assign(nk, 0); std::unordered_set kullanilanK; for (u32 o = 1; o <= nk; ++o) { KenarBilgi kb; if (!kenarBilgi(o, kb)) continue; const std::pair c = (o - 1 < mCift.size()) ? mCift[o - 1] : std::pair{0, 0}; u64 sec = 0; if (c.first || c.second) { // 1) topolojik: aynı yüz-KID çifti (en yakın orta ile ayır) f64 enYakin = 1e300; for (const auto& g : gk) if (g.cift == c && !kullanilanK.count(g.kid)) { const f64 d = (g.orta - kb.orta).uzunlukKare(); if (d < enYakin) { enYakin = d; sec = g.kid; } } } if (!sec) // 2) geometrik fallback: orta+uzunluk birebir (değişmeden geçen serbest kenar) for (const auto& g : gk) if (!kullanilanK.count(g.kid) && std::abs(g.uz - kb.uzunluk) < EPS && (g.orta - kb.orta).uzunlukKare() < EPS * EPS) { sec = g.kid; break; } if (sec && kullanilanK.insert(sec).second) ic.kenarKid[o - 1] = sec; } for (u32 i = 0; i < nk; ++i) if (ic.kenarKid[i] == 0) ic.kenarKid[i] = kaliciKimlikSonraki(); // --- NOKTA: TOPOLOJİK kimlik (bitişik yüz KID kümesi) + geometrik fallback --- // Köşe = ≥2 yüzün buluştuğu vertex → bitişik yüz KID kümesi ile kimliklenir; köşe ötelense de // yüzler kalıcı → küme aynı = AYNI etiket (kenar mantığının ikizi). Küme yoksa/eşleşmezse // geometrik konum eşleme (değişmeyen köşe), o da yoksa taze. const u32 nn = topExpSay(ic.sekil, TopAbs_VERTEX); ic.vertexKid.assign(nn, 0); const auto gImza = noktaYuzKidImza(girdi.icerik_->sekil, [&](u32 f) { return girdi.yuzKimlik(f); }); const auto mImza = noktaYuzKidImza(ic.sekil, [&](u32 f) { return yuzKimlik(f); }); std::unordered_set kullanilanN; for (u32 o = 1; o <= nn; ++o) { Vec3d p; if (!noktaKonum(o, p)) continue; if (o - 1 >= mImza.size()) continue; // güvence (nn == mImza.size(), olmamalı) u64 sec = 0; const std::vector& sg = mImza[o - 1]; if (sg.size() >= 2) { // 1) topolojik: aynı yüz-KID kümesi (en yakın konum ile ayır) f64 enYakin = 1e300; for (std::size_t g = 0; g < gImza.size(); ++g) if (gImza[g] == sg) { const u64 k = girdi.noktaKimlik((u32)(g + 1)); if (k && !kullanilanN.count(k)) { Vec3d gp; girdi.noktaKonum((u32)(g + 1), gp); const f64 d = (gp - p).uzunlukKare(); if (d < enYakin) { enYakin = d; sec = k; } } } } if (!sec) // 2) geometrik fallback: konum birebir (değişmeden geçen köşe) for (std::size_t g = 0; g < gNokta.size(); ++g) if ((gNokta[g] - p).uzunlukKare() < EPS * EPS) { const u64 k = girdi.noktaKimlik((u32)(g + 1)); if (k && !kullanilanN.count(k)) { sec = k; break; } } if (sec && kullanilanN.insert(sec).second) ic.vertexKid[o - 1] = sec; } for (u32 i = 0; i < nn; ++i) if (ic.vertexKid[i] == 0) ic.vertexKid[i] = kaliciKimlikSonraki(); } namespace { bool g_kaliciKimlikAcik = false; // varsayılan KAPALI (devasa-model O(N) etiketleme/serileştirme yükü) u64 g_kidSayac = 1; // BELGE-GENELİ global etiket sayacı (main-thread; op'lar tek-sıralı) } bool kaliciKimlikAcikMi() { return g_kaliciKimlikAcik; } void kaliciKimlikAyarla(bool acik) { g_kaliciKimlikAcik = acik; } u64 kaliciKimlikSonraki() { return g_kidSayac++; } void kaliciKimlikSayacEnAz(u64 enBuyukEtiket) { if (g_kidSayac <= enBuyukEtiket) g_kidSayac = enBuyukEtiket + 1; } bool BrepGovde::noktaKonum(u32 ordinal, Vec3d& konum) const { if (!gecerliMi()) return false; TopoDS_Shape s; if (!ordinalBulC(*icerik_,TopAbs_VERTEX, ordinal, s)) return false; konum = ceviri(BRep_Tool::Pnt(TopoDS::Vertex(s))); return true; } bool BrepGovde::kenarBilgi(u32 ordinal, KenarBilgi& bilgi) const { if (!gecerliMi()) return false; // TEMBEL önbellek (yüzle aynı kalıp): bir kez hesapla, sonrası kilitli okuma. { std::scoped_lock kilit(icerik_->yuzOnbKilit); if (icerik_->kenarBilgiOnb.empty()) { u32 n = 0; for (TopExp_Explorer k(icerik_->sekil, TopAbs_EDGE); k.More(); k.Next()) ++n; icerik_->kenarBilgiOnb.assign(n, {}); } if (ordinal >= 1 && ordinal <= icerik_->kenarBilgiOnb.size()) { const auto& e = icerik_->kenarBilgiOnb[ordinal - 1]; if (e.dolu) { bilgi = e.bilgi; return e.ok; } } } const auto kbT0 = std::chrono::steady_clock::now(); // TEŞHİS (>33 ms → log) TopoDS_Shape s; if (!ordinalBulC(*icerik_,TopAbs_EDGE, ordinal, s)) return false; struct KbOlcer { const std::chrono::steady_clock::time_point& t0; u32 ord; ~KbOlcer() { const f64 ms = std::chrono::duration(std::chrono::steady_clock::now() - t0).count(); if (ms > 33.0) gunluk::uyari("[perf] ADIM YAVAS: brep/kenarBilgi (ord {}) — {:.0f} ms", ord, ms); } } kbOlcer{kbT0, ordinal}; try { const TopoDS_Edge kenar = TopoDS::Edge(s); GProp_GProps oz; BRepGProp::LinearProperties(kenar, oz); bilgi.uzunluk = oz.Mass(); BRepAdaptor_Curve egri(kenar); const f64 u0 = egri.FirstParameter(), u1 = egri.LastParameter(); bilgi.bas = ceviri(egri.Value(u0)); bilgi.son = ceviri(egri.Value(u1)); bilgi.orta = ceviri(egri.Value((u0 + u1) * 0.5)); if (egri.GetType() == GeomAbs_Line) { bilgi.dogruMu = true; bilgi.yon = ceviri(egri.Line().Direction()); } else if (egri.GetType() == GeomAbs_Circle) { const gp_Circ c = egri.Circle(); bilgi.cemberMi = true; bilgi.yaricap = c.Radius(); bilgi.merkez = ceviri(c.Location()); bilgi.normal = ceviri(c.Axis().Direction()); } { std::scoped_lock kilit(icerik_->yuzOnbKilit); if (ordinal >= 1 && ordinal <= icerik_->kenarBilgiOnb.size()) { auto& e = icerik_->kenarBilgiOnb[ordinal - 1]; e.bilgi = bilgi; e.ok = true; e.dolu = true; } } return true; } catch (const Standard_Failure& hata) { gunluk::hata("Kenar bilgi hatasi: {}", hata.what()); return false; } } bool BrepGovde::kenarBilgiHizli(u32 ordinal, KenarBilgi& bilgi) const { // YALNIZ ÖNBELLEK OKUR (yuzBilgiHizli ikizi) — asla OCCT'ye inmez, asla bloklamaz. if (!gecerliMi() || ordinal == 0) return false; std::scoped_lock kilit(icerik_->yuzOnbKilit); if (ordinal > icerik_->kenarBilgiOnb.size()) return false; const auto& e = icerik_->kenarBilgiOnb[ordinal - 1]; if (!e.dolu) return false; bilgi = e.bilgi; return e.ok; } bool BrepGovde::kenarBilgiOnbellekteMi(u32 ordinal) const { if (!gecerliMi() || ordinal == 0) return false; std::scoped_lock kilit(icerik_->yuzOnbKilit); return ordinal <= icerik_->kenarBilgiOnb.size() && icerik_->kenarBilgiOnb[ordinal - 1].dolu; } namespace { // ---- Arka plan yüz-alanı hesabı: UI'yi ASLA bloklama ---- // Dev B-spline yüzde BRepGProp::SurfaceProperties ÖLÇÜLDÜ: ~1.1 s. Hover/bağlam gibi // GÖSTERİM yolları alanı beklerse tek kare saniyelerce donuyor. Bu sistem işi tek bir // arka plan işçisine verir; sonuç gelene dek alan "bilinmiyor" (0) döner, geldiğinde // yüz önbelleğine yazılır ve bir sonraki sorgu anında yanıtlanır. // Durum bilerek SIZDIRILIR (leak): süreç kapanırken işçi hâlâ bekliyor olabilir; statik // yıkım sırasıyla yarışmasın (çıkışta çökme kaynağı). struct AlanIs { std::shared_ptr govde; // shared_ptr TUTULUR → iş sırasında silinse bile geçerli u32 ordinal = 0; bool kenarMi = false; // false = yüz (yuzBilgi tam), true = kenar (kenarBilgi) }; struct AlanIsSistemi { std::mutex kilit; std::condition_variable kosul; std::deque kuyruk; std::unordered_set bekleyen; // (gövde ptr, ordinal, tür) → aynı iş iki kez girmesin bool isciVar = false; }; AlanIsSistemi& alanSis() { static AlanIsSistemi* s = new AlanIsSistemi(); return *s; } u64 alanAnahtar(u64 kimlik, u32 ordinal) { return (kimlik << 20) | (u64)(ordinal & 0xFFFFFu); } // TEŞHİS: yuzBilgi/kenarBilgi'nin HANGİ alt-adımı takılıyor (>33 ms → tek satır). struct Olcer { const char* ad; u32 ord; std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); ~Olcer() { const f64 ms = std::chrono::duration(std::chrono::steady_clock::now() - t0).count(); if (ms > 33.0) gunluk::uyari("[perf] ADIM YAVAS: brep/{} (ord {}) — {:.0f} ms", ad, ord, ms); } }; u64 isAnahtar(const BrepGovde* g, u32 ordinal, bool kenarMi) { return ((u64)reinterpret_cast(g) << 12) ^ (u64)ordinal ^ (kenarMi ? 0x8000000000000000ull : 0ull); } void alanIsciDongu() { AlanIsSistemi& s = alanSis(); for (;;) { AlanIs is; { std::unique_lock k(s.kilit); s.kosul.wait(k, [&] { return !s.kuyruk.empty(); }); is = std::move(s.kuyruk.front()); s.kuyruk.pop_front(); } // Gövdenin KENDİ önbelleğini doldurur (mutex'li) → bir sonraki hover sorgusu anında // yanıtlanır. Ana thread bu süre boyunca hiç beklemez. if (is.govde) { if (is.kenarMi) { KenarBilgi gecici; is.govde->kenarBilgi(is.ordinal, gecici); } else { YuzBilgi gecici; is.govde->yuzBilgi(is.ordinal, gecici, /*alanHesapla*/ true); } } std::scoped_lock k(s.kilit); s.bekleyen.erase(isAnahtar(is.govde.get(), is.ordinal, is.kenarMi)); } } void bilgiArkaPlanIste(std::shared_ptr govde, u32 ordinal, bool kenarMi) { if (!govde || ordinal == 0) return; AlanIsSistemi& s = alanSis(); const u64 anahtar = isAnahtar(govde.get(), ordinal, kenarMi); std::scoped_lock k(s.kilit); if (!s.bekleyen.insert(anahtar).second) return; // zaten kuyrukta s.kuyruk.push_back({std::move(govde), ordinal, kenarMi}); if (!s.isciVar) { s.isciVar = true; std::thread(alanIsciDongu).detach(); } s.kosul.notify_one(); } } // namespace void yuzBilgiArkaPlanIste(std::shared_ptr govde, u32 ordinal) { bilgiArkaPlanIste(std::move(govde), ordinal, /*kenarMi*/ false); } void kenarBilgiArkaPlanIste(std::shared_ptr govde, u32 ordinal) { bilgiArkaPlanIste(std::move(govde), ordinal, /*kenarMi*/ true); } bool BrepGovde::yuzBilgiHizli(u32 ordinal, YuzBilgi& bilgi) const { // YALNIZ ÖNBELLEK OKUR — hiçbir OCCT hesabı yapmaz, dolayısıyla ASLA bloklamaz. // Miss → false; çağıran yuzBilgiArkaPlanIste ile arka plan hesabını tetikler ve bir // sonraki karelerde değer hazır olur. (Ölçüldü: soğuk yüzde "ucuz" sanılan // BRepAdaptor/D1 dalı bile dev B-spline'da 1.4 s sürüyordu.) if (!gecerliMi() || ordinal == 0) return false; std::scoped_lock kilit(icerik_->yuzOnbKilit); if (ordinal > icerik_->yuzBilgiOnb.size()) return false; const auto& e = icerik_->yuzBilgiOnb[ordinal - 1]; if (!e.dolu) return false; bilgi = e.bilgi; return e.ok; } bool BrepGovde::yuzBilgiOnbellekteMi(u32 ordinal) const { if (!gecerliMi() || ordinal == 0) return false; std::scoped_lock kilit(icerik_->yuzOnbKilit); return ordinal <= icerik_->yuzBilgiOnb.size() && icerik_->yuzBilgiOnb[ordinal - 1].dolu; } bool BrepGovde::yuzBilgi(u32 ordinal, YuzBilgi& bilgi, bool alanHesapla) const { if (!gecerliMi()) return false; // Önbellek (akıllı seçim hızlandırma): warm ise doğrudan dön. Cache alanı YALNIZ düzlemde // tutar (B-spline alanı pahalı, benzerYuzler eğriyi yarıçapla karşılaştırır). alan İSTENİP // cache'te yoksa (non-düzlem) hesaplamaya düş → alan sorgusu (ölçüm) doğru kalır. // TEMBEL önbellek: prewarm (yuzBilgiOnbellekKur) her yolda koşmuyor — ör. STEP .fkcache'ten // "anında" yüklemede hiç çağrılmıyordu → pahalı alan hesabı HER sorguda tekrarlanıyordu // (ölçüldü: ordinal 115 = 1.1 s, tek karede 3 kez). Tablo burada bir kez boyutlanır, // girişler tek tek dolar; prewarm'dan bağımsız çalışır. { std::scoped_lock kilit(icerik_->yuzOnbKilit); if (icerik_->yuzBilgiOnb.empty()) { u32 n = 0; for (TopExp_Explorer k(icerik_->sekil, TopAbs_FACE); k.More(); k.Next()) ++n; icerik_->yuzBilgiOnb.assign(n, {}); } if (ordinal >= 1 && ordinal <= icerik_->yuzBilgiOnb.size()) { const auto& e = icerik_->yuzBilgiOnb[ordinal - 1]; if (e.dolu && (!alanHesapla || !e.ok || e.alanHazir)) { bilgi = e.bilgi; return e.ok; } // alanHesapla=true + non-düzlem + alan yok → aşağıda hesapla (sonra cache'e yazılır). } } TopoDS_Shape s; { Olcer o{"yuzBilgi: ordinalBul", ordinal}; if (!ordinalBulC(*icerik_, TopAbs_FACE, ordinal, s)) return false; } // Mesh-temelli (yüzeysiz VRML/STL) yüz: BRepAdaptor_Surface/GProp NULL yüzeyde // SEGFAULT eder (Standard_Failure DEĞİL → try yakalamaz). Yüzey yoksa triangulation'dan // ANALİTİK TANIMA (düzlem/silindir/küre fit) → hover kartı/ölçüm/benzerYuzler/çipler // STEP'teki gibi çalışır. O(yüz düğümü) → hover'da µs. if (BRep_Tool::Surface(TopoDS::Face(s)).IsNull()) { Olcer o{"yuzBilgi: meshYuzBilgi (yuzeysiz)", ordinal}; return meshYuzBilgi(TopoDS::Face(s), bilgi); } Olcer oAdaptor{"yuzBilgi: adaptor/D1 (ucuz dal)", ordinal}; try { const TopoDS_Face yuz = TopoDS::Face(s); // Alan + ağırlık merkezi PAHALI (devasa/B-spline yüzde yüzlerce ms). Hover gibi hızlı // sorgular alanHesapla=false verir → yalnız tip/eksen/yarıçap (BRepAdaptor, ucuz). if (alanHesapla) { // TEŞHİS: dev B-spline yüzde SurfaceProperties saniyeler sürüyor (ölçüldü: hover/seçim // karesinde 1.1-4.7 s donma). Eşik üstü çağrı log'a düşer → hangi yolun alanı BOŞUNA // istediği görünür (o çağıran alanHesapla=false vermeli). const auto t0 = std::chrono::steady_clock::now(); GProp_GProps oz; BRepGProp::SurfaceProperties(yuz, oz); bilgi.alan = oz.Mass(); bilgi.merkez = ceviri(oz.CentreOfMass()); const f64 ms = std::chrono::duration(std::chrono::steady_clock::now() - t0).count(); if (ms > 50.0) { gunluk::uyari("[perf] yuzBilgi alan hesabi YAVAS: {:.0f} ms (yuz ordinal {}) — cagiran:\n{}", ms, ordinal, std::stacktrace::current(1, 8)); } } BRepAdaptor_Surface yuzey(yuz); if (yuzey.GetType() == GeomAbs_Plane) { bilgi.duzlemMi = true; bilgi.normal = ceviri(yuzey.Plane().Axis().Direction()); if (yuz.Orientation() == TopAbs_REVERSED) bilgi.normal = bilgi.normal * -1.0; } else if (yuzey.GetType() == GeomAbs_Cylinder || (yuzey.GetType() == GeomAbs_SurfaceOfExtrusion && !yuzey.BasisCurve().IsNull() && yuzey.BasisCurve()->GetType() == GeomAbs_Circle)) { // Canonical silindir VEYA sketch'ten extrude edilen DAİRE (SurfaceOfExtrusion, taban çember) // → ikisi de radyal push/pull için silindir. Aksi halde extrude-daire silindir sayılmıyordu. if (yuzey.GetType() == GeomAbs_Cylinder) { const gp_Cylinder sl = yuzey.Cylinder(); bilgi.yaricap = sl.Radius(); bilgi.eksen = ceviri(sl.Axis().Direction()); bilgi.eksenNokta = ceviri(sl.Location()); } else { const gp_Circ cc = yuzey.BasisCurve()->Circle(); bilgi.yaricap = cc.Radius(); bilgi.eksen = ceviri(yuzey.Direction()); bilgi.eksenNokta = ceviri(cc.Location()); } bilgi.silindirMi = true; // Push/Pull gizmosu için: merkez (ağırlık) tam silindirde eksen üstünde kalır → // yüzey-üstü örnek nokta (UV ortası) + dış radyal normal (orientation dahil: delik→içe). const f64 u0 = yuzey.FirstUParameter(), u1 = yuzey.LastUParameter(); const f64 v0 = yuzey.FirstVParameter(), v1 = yuzey.LastVParameter(); gp_Pnt yp; gp_Vec dU, dV; yuzey.D1(0.5 * (u0 + u1), 0.5 * (v0 + v1), yp, dU, dV); bilgi.merkez = ceviri(yp); const Vec3d eks = bilgi.eksen.birim(); const Vec3d rel = bilgi.merkez - bilgi.eksenNokta; const Vec3d rad = rel - eks * rel.nokta(eks); if (rad.uzunlukKare() > 1e-18) { bilgi.normal = rad.birim(); // İşaret GERÇEK yüzey normalinden (dU×dV, orientation dahil): extrude-daire duvarında // doğal normal radyal-dış olmayabilir → ham orientation-flip yanlış işaret veriyordu // (commit silindirRadyalOfset ile aynı mantık; delik→içe, mil→dışa birebir tutarlı). gp_Vec nrm = dU.Crossed(dV); if (yuz.Orientation() == TopAbs_REVERSED) nrm.Reverse(); if (nrm.Magnitude() > 1e-12) { if (nrm.X() * rad.x + nrm.Y() * rad.y + nrm.Z() * rad.z < 0.0) bilgi.normal = bilgi.normal * -1.0; } else if (yuz.Orientation() == TopAbs_REVERSED) { bilgi.normal = bilgi.normal * -1.0; // dejenere D1 → eski orientation-flip fallback } } } else if (yuzey.GetType() == GeomAbs_Sphere) { const gp_Sphere kr = yuzey.Sphere(); bilgi.kureMi = true; bilgi.yaricap = kr.Radius(); bilgi.eksenNokta = ceviri(kr.Location()); } else if (yuzey.GetType() == GeomAbs_Cone) { const gp_Cone kn = yuzey.Cone(); bilgi.koniMi = true; bilgi.yaricap = kn.RefRadius(); bilgi.eksen = ceviri(kn.Axis().Direction()); bilgi.eksenNokta = ceviri(kn.Axis().Location()); // Push/Pull gizmosu: yüzey-üstü örnek nokta (UV ortası) + dış radyal normal (koni normali // semiAngle eğik ama commit RADYAL offset yapar → gizmo radyal ile tutarlı; orientation delik→içe). const f64 u0 = yuzey.FirstUParameter(), u1 = yuzey.LastUParameter(); const f64 v0 = yuzey.FirstVParameter(), v1 = yuzey.LastVParameter(); const gp_Pnt yp = yuzey.Value(0.5 * (u0 + u1), 0.5 * (v0 + v1)); bilgi.merkez = ceviri(yp); const Vec3d eks = bilgi.eksen.birim(); const Vec3d rel = bilgi.merkez - bilgi.eksenNokta; const Vec3d rad = rel - eks * rel.nokta(eks); if (rad.uzunlukKare() > 1e-18) { bilgi.normal = rad.birim(); if (yuz.Orientation() == TopAbs_REVERSED) bilgi.normal = bilgi.normal * -1.0; } } else if (yuzey.GetType() == GeomAbs_Torus) { // Torus = fillet/pah taşıyıcı yüzeyi. SALT BİLGİ: merkez/normal BİLEREK // yazılmaz — push/pull (torusu zaten dışlar) ve ordinal-imza eşlemesi torusu // "diğer" sayar; onları olduğu yerde bırakmak davranışı birebir korur. const gp_Torus tr = yuzey.Torus(); bilgi.torusMu = true; bilgi.yaricap = tr.MajorRadius(); bilgi.kucukYaricap = tr.MinorRadius(); bilgi.eksen = ceviri(tr.Axis().Direction()); bilgi.eksenNokta = ceviri(tr.Location()); } else { // Genel yüzey (B-spline/Bezier/offset — GSD şişir/reshape SONUCU): analitik değil → // normal hiçbir daldan atanmıyordu, SIFIR kalıyordu. Bu yüzden şişmiş yüzeyde gizmo oku // çizilemiyor + yuzeySisir erken çıkıyordu ("hiç bişi yapamıyoruz"). UV-ortası GERÇEK // yüzey normali (D1 → dU×dV, orientation dahil) + temsil noktası (silindir/koni ile aynı kalıp). const f64 u0 = yuzey.FirstUParameter(), u1 = yuzey.LastUParameter(); const f64 v0 = yuzey.FirstVParameter(), v1 = yuzey.LastVParameter(); gp_Pnt yp; gp_Vec dU, dV; yuzey.D1(0.5 * (u0 + u1), 0.5 * (v0 + v1), yp, dU, dV); bilgi.merkez = ceviri(yp); // yüzey-üstü temsil noktası (kütle merkezi eğri yüzde içeride kalır) gp_Vec nrm = dU.Crossed(dV); if (yuz.Orientation() == TopAbs_REVERSED) nrm.Reverse(); if (nrm.Magnitude() > 1e-12) bilgi.normal = ceviri(nrm.Normalized()); } // alanHesapla=false: merkez SurfaceProperties'ten gelirdi (PAHALI, atlandı) → yüzey-üstü // UCUZ temsil noktası (UV-orta, tek Value). Gizmo/hover HER KARE çağırır; B-spline'da // (şişmiş/kenar-kaydırılmış GSD yüzey) kare başına yüzlerce ms = DONMA buydu. if (!alanHesapla) { const Vec3d& m = bilgi.merkez; if (m.x * m.x + m.y * m.y + m.z * m.z < 1e-24) bilgi.merkez = ceviri(yuzey.Value( 0.5 * (yuzey.FirstUParameter() + yuzey.LastUParameter()), 0.5 * (yuzey.FirstVParameter() + yuzey.LastVParameter()))); } // Pahalı alan/merkez BİR KEZ hesaplansın: non-düzlem alan önbelleğe YAZILMIYORDU → // aynı yüz her sorguda BRepGProp'u yeniden koşturuyordu (ölçüldü: ordinal 115 = 1.1 s, // tek karede 3 kez → 3.3 s donma). Artık ilk hesaptan sonra cache'ten döner. { std::scoped_lock kilit(icerik_->yuzOnbKilit); if (ordinal >= 1 && ordinal <= icerik_->yuzBilgiOnb.size()) { auto& e = icerik_->yuzBilgiOnb[ordinal - 1]; e.bilgi = bilgi; e.ok = true; e.dolu = true; if (alanHesapla) e.alanHazir = true; } } return true; } catch (const Standard_Failure& hata) { gunluk::hata("Yuz bilgi hatasi: {}", hata.what()); return false; } } void BrepGovde::yuzBilgiOnbellekKur() const { if (!gecerliMi() || icerik_->yuzBilgiOnbHazir) return; u32 n = 0; for (TopExp_Explorer k(icerik_->sekil, TopAbs_FACE); k.More(); k.Next()) ++n; icerik_->yuzBilgiOnb.assign(n, {}); // yuzBilgiOnbHazir HÂLÂ false → aşağıdaki yuzBilgi çağrıları HESAPLAR (cache'e düşmez). // Alan (BRepGProp) B-spline yüzde YÜZLERCE ms → önce alanHesapla=false (ucuz: tip+yarıçap), // alanı YALNIZ DÜZLEMDE hesapla (benzerYuzler düzlemi alanla, eğriyi yarıçapla karşılaştırır). for (u32 i = 1; i <= n; ++i) { auto& e = icerik_->yuzBilgiOnb[i - 1]; e.ok = yuzBilgi(i, e.bilgi, /*alanHesapla*/ false); if (e.ok && e.bilgi.duzlemMi && e.bilgi.alan == 0.0) { YuzBilgi tam; if (yuzBilgi(i, tam, /*alanHesapla*/ true)) e.bilgi = tam; // düzlem alanı ucuz } } icerik_->yuzBilgiOnbHazir = true; // artık yuzBilgi cache'ten döner } std::vector BrepGovde::yuzNoktalari(u32 ordinal) const { std::vector r; if (!gecerliMi()) return r; TopoDS_Shape s; if (!ordinalBulC(*icerik_,TopAbs_FACE, ordinal, s)) return r; try { TopLoc_Location loc; // Mevcut ucgenleme dugumleri (Dugum olusturulurken zaten uretildi → yeniden mesh yok) const Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(TopoDS::Face(s), loc); if (tri.IsNull()) return r; const gp_Trsf t = loc.Transformation(); const int n = tri->NbNodes(); const int adim = std::max(1, n / 400); // ~400 nokta ust siniri (en kisa/uzak perf) for (int i = 1; i <= n; i += adim) { const gp_Pnt p = tri->Node(i).Transformed(t); r.push_back({p.X(), p.Y(), p.Z()}); } } catch (const Standard_Failure&) {} return r; } bool BrepGovde::yuzNoktaNormali(u32 ordinal, Vec3d yerelNokta, Vec3d& normal) const { normal = {0, 0, 0}; if (!gecerliMi()) return false; TopoDS_Shape s; if (!ordinalBulC(*icerik_,TopAbs_FACE, ordinal, s)) return false; try { const TopoDS_Face yuz = TopoDS::Face(s); // BRep_Tool::Surface(yuz, loc): loc yerlestirme ile yuzey. Nokta zaten // yerlestirilmis (yuzNoktalari transformed dondurur) → loc'u nokta uzayina // uygula, sonra ayni yuzeyde (u,v) parametresini cozup normali al. Boylece // projeksiyon ve normal ayni param uzayinda tutarli. TopLoc_Location loc; const Handle(Geom_Surface) yuzey = BRep_Tool::Surface(yuz, loc); if (yuzey.IsNull()) return false; gp_Pnt hedef(yerelNokta.x, yerelNokta.y, yerelNokta.z); hedef.Transform(loc.Transformation().Inverted()); // yerlestirmeyi geri al GeomAPI_ProjectPointOnSurf izd(hedef, yuzey); if (!izd.IsDone() || izd.NbPoints() < 1) return false; f64 u = 0, v = 0; izd.LowerDistanceParameters(u, v); // Normal yuzeyin kendi (yerlestirmesiz) frame'inde → tekrar loc uygula. GeomLProp_SLProps prop(yuzey, u, v, 1, 1e-7); if (!prop.IsNormalDefined()) return false; gp_Dir nd = prop.Normal(); nd.Transform(loc.Transformation()); // yerlestirme yonelimini geri uygula if (yuz.Orientation() == TopAbs_REVERSED) nd.Reverse(); normal = ceviri(nd); return true; } catch (const Standard_Failure&) { return false; } } std::vector> BrepGovde::yuzKonturlari(u32 ordinal, f64 sapma) const { std::vector> r; if (!gecerliMi()) return r; TopoDS_Shape s; if (!ordinalBulC(*icerik_,TopAbs_FACE, ordinal, s)) return r; try { const TopoDS_Face yuz = TopoDS::Face(s); const TopoDS_Wire disWire = BRepTools::OuterWire(yuz); const auto wireKonturu = [&](const TopoDS_Wire& w) { std::vector kontur; for (BRepTools_WireExplorer we(w, yuz); we.More(); we.Next()) { const TopoDS_Edge kenar = we.Current(); BRepAdaptor_Curve egri(kenar); GCPnts_QuasiUniformDeflection ayrik(egri, sapma); if (!ayrik.IsDone() || ayrik.NbPoints() < 2) continue; const bool ters = kenar.Orientation() == TopAbs_REVERSED; const int n = ayrik.NbPoints(); // Son nokta bir sonraki kenarin ilkiyle cakisir → atlanir (kapali polyline). for (int i = 1; i < n; ++i) { const gp_Pnt p = ayrik.Value(ters ? n - i + 1 : i); kontur.push_back({p.X(), p.Y(), p.Z()}); } } return kontur; }; // Ilk kontur dis sinir; ic wire'lar (delik/ada) sonra. if (std::vector dis = wireKonturu(disWire); dis.size() >= 3) r.push_back(std::move(dis)); for (TopExp_Explorer k(yuz, TopAbs_WIRE); k.More(); k.Next()) { const TopoDS_Wire w = TopoDS::Wire(k.Current()); if (w.IsSame(disWire)) continue; if (std::vector ic = wireKonturu(w); ic.size() >= 3) r.push_back(std::move(ic)); } } catch (const Standard_Failure& hata) { gunluk::hata("Yuz kontur hatasi: {}", hata.what()); } return r; } std::vector BrepGovde::kenarNoktalari(u32 ordinal) const { std::vector r; if (!gecerliMi()) return r; TopoDS_Shape s; if (!ordinalBulC(*icerik_,TopAbs_EDGE, ordinal, s)) return r; try { BRepAdaptor_Curve egri(TopoDS::Edge(s)); const f64 u0 = egri.FirstParameter(), u1 = egri.LastParameter(); const int n = 24; for (int i = 0; i <= n; ++i) { const gp_Pnt p = egri.Value(u0 + (u1 - u0) * (f64)i / n); r.push_back({p.X(), p.Y(), p.Z()}); } } catch (const Standard_Failure&) {} return r; } std::vector> BrepGovde::tumKenarKomsuYuzler() const { std::vector> r; if (!gecerliMi()) return r; try { // kenarKomsuYuzler'in TOPLU ikizi: ata haritasi + yuz-ordinal indeksi BIR kez // kurulur (tekil cagri kenar basina tum modeli tariyordu → O(E·(E+F²)), // keskin kenar/sureklilik gibi tum-kenar taramalarinda dakikalara cikar). // Ordinal semantigi tekil cagriyla BIREBIR: TopExp_Explorer sirasi, yuz // ordinali ilk-gorulme indeksi (IndexedMap.Add ayni TShape'e ayni indeksi verir). TopTools_IndexedDataMapOfShapeListOfShape harita; TopExp::MapShapesAndAncestors(icerik_->sekil, TopAbs_EDGE, TopAbs_FACE, harita); TopTools_IndexedMapOfShape yuzIdx; for (TopExp_Explorer k(icerik_->sekil, TopAbs_FACE); k.More(); k.Next()) yuzIdx.Add(k.Current()); for (TopExp_Explorer k(icerik_->sekil, TopAbs_EDGE); k.More(); k.Next()) { std::vector yuzler; if (harita.Contains(k.Current())) for (const TopoDS_Shape& yuzS : harita.FindFromKey(k.Current())) { const int idx = yuzIdx.FindIndex(yuzS); if (idx > 0) yuzler.push_back((u32)idx); } r.push_back(std::move(yuzler)); } } catch (const Standard_Failure&) { r.clear(); } return r; } std::vector BrepGovde::kenarKomsuYuzler(u32 kenarOrdinal) const { std::vector r; if (!gecerliMi()) return r; TopoDS_Shape kenarS; if (!ordinalBulC(*icerik_,TopAbs_EDGE, kenarOrdinal, kenarS)) return r; try { // Kenar → ata (yuz) haritasi. Sonra bulunan yuzleri ordinallerine cevir // (yuzBilgi/secim ile ayni TopExp sirasi → tutarli). TopTools_IndexedDataMapOfShapeListOfShape harita; TopExp::MapShapesAndAncestors(icerik_->sekil, TopAbs_EDGE, TopAbs_FACE, harita); if (!harita.Contains(kenarS)) return r; const TopTools_ListOfShape& yuzler = harita.FindFromKey(kenarS); for (const TopoDS_Shape& yuzS : yuzler) { u32 ord = 0, i = 0; for (TopExp_Explorer k(icerik_->sekil, TopAbs_FACE); k.More(); k.Next()) { ++i; if (k.Current().IsSame(yuzS)) { ord = i; break; } } if (ord) r.push_back(ord); } } catch (const Standard_Failure&) {} return r; } std::vector BrepGovde::yuzKomsuYuzler(u32 yuzOrdinal) const { std::vector r; if (!gecerliMi()) return r; TopoDS_Shape yuzS; if (!ordinalBulC(*icerik_, TopAbs_FACE, yuzOrdinal, yuzS)) return r; try { // Yüzün her kenarını paylaşan DİĞER yüzler (kenar→yüz ata haritası). kenarKomsuYuzler'in // yüz-duali; topolojik komşuluk (defeature/yayılım analizi temeli). Ordinal = TopExp sırası. TopTools_IndexedDataMapOfShapeListOfShape kenarYuz; TopExp::MapShapesAndAncestors(icerik_->sekil, TopAbs_EDGE, TopAbs_FACE, kenarYuz); // Sonuç yüz TShape'lerini biriktir (tekrarsız), sonra ordinale çevir. TopTools_IndexedMapOfShape komsuSet; for (TopExp_Explorer ke(yuzS, TopAbs_EDGE); ke.More(); ke.Next()) { if (!kenarYuz.Contains(ke.Current())) continue; for (const TopoDS_Shape& ky : kenarYuz.FindFromKey(ke.Current())) if (!ky.IsSame(yuzS)) komsuSet.Add(ky); // kendisi hariç } if (komsuSet.IsEmpty()) return r; u32 i = 0; for (TopExp_Explorer k(icerik_->sekil, TopAbs_FACE); k.More(); k.Next()) { ++i; if (komsuSet.Contains(k.Current())) r.push_back(i); } } catch (const Standard_Failure&) {} return r; } std::vector BrepGovde::koplanarYuzler(u32 yuzOrdinal, f64 aciTolDeg) const { std::vector sonuc; if (!gecerliMi()) return sonuc; YuzBilgi y0; if (!yuzBilgi(yuzOrdinal, y0)) return sonuc; if (!y0.duzlemMi) { sonuc.push_back(yuzOrdinal); return sonuc; } // yalnız düz yüz yayılır try { const TopoDS_Shape& s = icerik_->sekil; TopTools_IndexedDataMapOfShapeListOfShape kenarYuz; TopExp::MapShapesAndAncestors(s, TopAbs_EDGE, TopAbs_FACE, kenarYuz); TopTools_IndexedMapOfShape yuzIdx; // yüz(IsSame) → 1-tabanlı ordinal (TopExp sırası) for (TopExp_Explorer fe(s, TopAbs_FACE); fe.More(); fe.Next()) yuzIdx.Add(fe.Current()); const f64 cosTol = std::cos(aciTolDeg * 3.14159265358979323846 / 180.0); std::unordered_set ziyaret{yuzOrdinal}; std::vector yigin{yuzOrdinal}; while (!yigin.empty()) { const u32 cur = yigin.back(); yigin.pop_back(); sonuc.push_back(cur); TopoDS_Shape curFace; if (!ordinalBulC(*icerik_, TopAbs_FACE, cur, curFace)) continue; for (TopExp_Explorer ke(curFace, TopAbs_EDGE); ke.More(); ke.Next()) { if (!kenarYuz.Contains(ke.Current())) continue; for (const TopoDS_Shape& komsuF : kenarYuz.FindFromKey(ke.Current())) { const int ko = yuzIdx.FindIndex(komsuF); if (ko < 1 || ziyaret.count((u32)ko)) continue; YuzBilgi yk; if (!yuzBilgi((u32)ko, yk) || !yk.duzlemMi) continue; if (std::abs(yk.normal.nokta(y0.normal)) >= cosTol) { // paralel/koplanar ziyaret.insert((u32)ko); yigin.push_back((u32)ko); } } } } } catch (const Standard_Failure&) {} return sonuc; } std::vector BrepGovde::tanjantYuzler(u32 yuzOrdinal, f64 aciTolDeg) const { // Plasticity tangent-select: yüzden başlayıp ortak kenarda G1-TEĞET (normaller kenar // orta noktasında hizalı) komşu yüzlere yayıl. koplanarYuzler'in aksine EĞRİ yüz de // dahil (fillet düz komşusuna teğet). Kenar-yerel normal (global değil) → fillet zinciri. std::vector sonuc; if (!gecerliMi()) return sonuc; TopoDS_Shape seed; if (!ordinalBulC(*icerik_, TopAbs_FACE, yuzOrdinal, seed)) return sonuc; try { const TopoDS_Shape& s = icerik_->sekil; TopTools_IndexedDataMapOfShapeListOfShape kenarYuz; TopExp::MapShapesAndAncestors(s, TopAbs_EDGE, TopAbs_FACE, kenarYuz); TopTools_IndexedMapOfShape yuzIdx; for (TopExp_Explorer fe(s, TopAbs_FACE); fe.More(); fe.Next()) yuzIdx.Add(fe.Current()); const f64 cosTol = std::cos(aciTolDeg * 3.14159265358979323846 / 180.0); // Yüzün kenar orta noktasındaki DIŞ normali (orientation dahil). const auto kenarNormali = [](const TopoDS_Face& f, const TopoDS_Edge& e, gp_Dir& n) -> bool { Standard_Real a, b; Handle(Geom2d_Curve) pc = BRep_Tool::CurveOnSurface(e, f, a, b); if (pc.IsNull()) return false; gp_Pnt2d uv; pc->D0(0.5 * (a + b), uv); BRepAdaptor_Surface sa(f); BRepLProp_SLProps p(sa, uv.X(), uv.Y(), 1, 1e-7); if (!p.IsNormalDefined()) return false; gp_Dir d = p.Normal(); if (f.Orientation() == TopAbs_REVERSED) d.Reverse(); n = d; return true; }; std::unordered_set ziyaret{yuzOrdinal}; std::vector yigin{yuzOrdinal}; while (!yigin.empty()) { const u32 cur = yigin.back(); yigin.pop_back(); sonuc.push_back(cur); TopoDS_Shape curS; if (!ordinalBulC(*icerik_, TopAbs_FACE, cur, curS)) continue; const TopoDS_Face curF = TopoDS::Face(curS); for (TopExp_Explorer ke(curF, TopAbs_EDGE); ke.More(); ke.Next()) { const TopoDS_Edge e = TopoDS::Edge(ke.Current()); if (!kenarYuz.Contains(e)) continue; gp_Dir nCur; if (!kenarNormali(curF, e, nCur)) continue; for (const TopoDS_Shape& komsuF : kenarYuz.FindFromKey(e)) { const int ko = yuzIdx.FindIndex(komsuF); if (ko < 1 || ziyaret.count((u32)ko)) continue; gp_Dir nK; if (!kenarNormali(TopoDS::Face(komsuF), e, nK)) continue; if (std::abs(nCur.Dot(nK)) >= cosTol) { // kenarda G1-teğet → aynı yüzey grubu ziyaret.insert((u32)ko); yigin.push_back((u32)ko); } } } } } catch (const Standard_Failure&) {} return sonuc; } KenarDihedralBilgi BrepGovde::kenarDihedral(u32 kenarOrdinal) const { const std::vector yuzler = kenarKomsuYuzler(kenarOrdinal); if (yuzler.size() < 2) return {}; // sinir/serbest kenar → dihedral tanimsiz return kenarDihedral(kenarOrdinal, yuzler[0], yuzler[1]); } namespace { // Yuz normal baglami: yuzNoktaNormali her ornekte Surface + projektor (Extrema) // kuruyordu → kenar basina 16 kurulum. Burada yuz basina BIR kez kurulur, 8 ornek // Perform/SetParameters ile yeniden kullanir. Isaret/yerlestirme kurallari // yuzNoktaNormali ile birebir (ayni Bounds + Confusion cozunurlugu). struct YuzNormalBaglam { Handle(Geom_Surface) yuzey; TopLoc_Location loc; bool tersYuz = false; GeomAPI_ProjectPointOnSurf izd; std::unique_ptr prop; bool kur(const BrepGovde::Icerik& ic, u32 ord) { TopoDS_Shape s; if (!ordinalBulC(ic, TopAbs_FACE, ord, s)) return false; const TopoDS_Face yuz = TopoDS::Face(s); yuzey = BRep_Tool::Surface(yuz, loc); if (yuzey.IsNull()) return false; tersYuz = yuz.Orientation() == TopAbs_REVERSED; Standard_Real u1, u2, v1, v2; yuzey->Bounds(u1, u2, v1, v2); // ctor(P,Surface) da ayni dogal sinirlari kullanir izd.Init(yuzey, u1, u2, v1, v2); prop = std::make_unique(yuzey, 1, 1e-7); return true; } bool normal(const gp_Pnt& yerlesikP, Vec3d& n) { gp_Pnt hedef = yerlesikP; hedef.Transform(loc.Transformation().Inverted()); izd.Perform(hedef); if (!izd.IsDone() || izd.NbPoints() < 1) return false; f64 u = 0, v = 0; izd.LowerDistanceParameters(u, v); prop->SetParameters(u, v); if (!prop->IsNormalDefined()) return false; gp_Dir nd = prop->Normal(); nd.Transform(loc.Transformation()); if (tersYuz) nd.Reverse(); n = ceviri(nd); return true; } }; } // namespace KenarDihedralBilgi BrepGovde::kenarDihedral(u32 kenarOrdinal, u32 yuzA, u32 yuzB) const { KenarDihedralBilgi b; if (!gecerliMi() || !yuzA || !yuzB) return b; TopoDS_Shape kenarS; if (!ordinalBulC(*icerik_, TopAbs_EDGE, kenarOrdinal, kenarS)) return b; const u32 yuzler[2] = {yuzA, yuzB}; try { TopoDS_Shape yuzAS; if (!ordinalBulC(*icerik_, TopAbs_FACE, yuzler[0], yuzAS)) return b; // Disbukeylik icin teget, kenarin YUZ A wire'indaki yonunde olmali ("malzeme solda" // kurali): yuz A icindeki occurrence REVERSED ise egri tegeti cevrilir. yuzNoktaNormali // yuz orientasyonunu normale ayni bicimde isler → isaret kurali ikisiyle tutarli. bool ters = false; for (TopExp_Explorer ke(yuzAS, TopAbs_EDGE); ke.More(); ke.Next()) if (ke.Current().IsSame(kenarS)) { ters = ke.Current().Orientation() == TopAbs_REVERSED; break; } YuzNormalBaglam bagA, bagB; if (!bagA.kur(*icerik_, yuzler[0]) || !bagB.kur(*icerik_, yuzler[1])) return b; BRepAdaptor_Curve egri(TopoDS::Edge(kenarS)); const f64 u0 = egri.FirstParameter(), u1 = egri.LastParameter(); f64 maksAci = 0.0; int disOy = 0, icOy = 0; u32 ornek = 0; const int n = 9; // 8 ic ornek, uc noktalar atlanir for (int i = 1; i < n; ++i) { gp_Pnt p; gp_Vec d1; egri.D1(u0 + (u1 - u0) * (f64)i / n, p, d1); Vec3d na, nb; if (!bagA.normal(p, na)) continue; if (!bagB.normal(p, nb)) continue; const f64 la = std::sqrt(na.uzunlukKare()), lb = std::sqrt(nb.uzunlukKare()); if (la < 1e-9 || lb < 1e-9) continue; const f64 kos = std::clamp(na.nokta(nb) / (la * lb), -1.0, 1.0); maksAci = std::max(maksAci, std::acos(kos) * (180.0 / 3.14159265358979323846)); Vec3d t{d1.X(), d1.Y(), d1.Z()}; if (ters) t = {-t.x, -t.y, -t.z}; const f64 isaret = na.capraz(nb).nokta(t); if (isaret > 0) ++disOy; else if (isaret < 0) ++icOy; ++ornek; } if (ornek == 0) return b; b.gecerli = true; b.aciDeg = maksAci; // Ornek basina isaret oyu (tegete yakin kenarda gurultuye dayaniklilik): cogunluk kazanir. b.disbukey = disOy >= icOy; } catch (const Standard_Failure&) { b.gecerli = false; } return b; } std::vector BrepGovde::kenarZinciri(u32 kenarOrdinal, f64 aciTolDeg) const { std::vector sonuc; if (!gecerliMi()) return sonuc; // GERÇEK uç teğetleriyle zincir (CATIA tangency propagation): her kenarın iki ucundaki // eğri teğeti (D1) alınır — DÜZ kenarlarda eski davranışla birebir (teğet=doğrultu), // EĞRİ kenarlar (fillet yayı vb.) da teğet-sürekli uçta zincire katılır → çevre // fillet'i tek Alt+tık jestiyle seçilir. struct Uc { Vec3d bas, son, tBas, tSon; bool var = false; }; std::vector uc; try { for (TopExp_Explorer k(icerik_->sekil, TopAbs_EDGE); k.More(); k.Next()) { Uc u; try { BRepAdaptor_Curve c(TopoDS::Edge(k.Current())); gp_Pnt pb, ps; gp_Vec vb, vs; c.D1(c.FirstParameter(), pb, vb); c.D1(c.LastParameter(), ps, vs); if (vb.Magnitude() > 1e-12 && vs.Magnitude() > 1e-12) { u.bas = {pb.X(), pb.Y(), pb.Z()}; u.son = {ps.X(), ps.Y(), ps.Z()}; u.tBas = Vec3d{vb.X(), vb.Y(), vb.Z()}.birim(); u.tSon = Vec3d{vs.X(), vs.Y(), vs.Z()}.birim(); u.var = true; } } catch (const Standard_Failure&) {} uc.push_back(u); } } catch (const Standard_Failure&) { return sonuc; } if (kenarOrdinal == 0 || kenarOrdinal > uc.size()) return sonuc; sonuc.push_back(kenarOrdinal); if (!uc[kenarOrdinal - 1].var) return sonuc; // dejenere kenar → yalnız kendisi const f64 cosTol = std::cos(aciTolDeg * 3.14159265358979323846 / 180.0); const f64 noktaTol2 = 1e-8; // uç çakışma (mm²) std::unordered_set ziyaret{kenarOrdinal}; const Uc& u0 = uc[kenarOrdinal - 1]; // İki uçtan yayıl: 'yon' = mevcut uçtan ZİNCİR DEVAM yönü (uçtan dışa birim teğet). for (int e = 0; e < 2; ++e) { Vec3d nokta = e ? u0.son : u0.bas; Vec3d yon = e ? u0.tSon : (u0.tBas * -1.0); bool devam = true; while (devam) { devam = false; for (u32 j = 1; j <= (u32)uc.size(); ++j) { if (ziyaret.count(j) || !uc[j - 1].var) continue; const Uc& kj = uc[j - 1]; const bool basC = (kj.bas - nokta).uzunlukKare() < noktaTol2; const bool sonC = !basC && (kj.son - nokta).uzunlukKare() < noktaTol2; if (!basC && !sonC) continue; const Vec3d giris = basC ? kj.tBas : (kj.tSon * -1.0); // bu uçtan İÇERİ teğet if (yon.nokta(giris) < cosTol) continue; // teğet süreklilik yok ziyaret.insert(j); sonuc.push_back(j); nokta = basC ? kj.son : kj.bas; yon = basC ? kj.tSon : (kj.tBas * -1.0); devam = true; break; } } } return sonuc; } std::vector BrepGovde::paralelYuzler(u32 yuzOrdinal, f64 aciTolDeg) const { std::vector sonuc; if (!gecerliMi()) return sonuc; yuzBilgiOnbellekKur(); // tüm-yüz taraması → cache warm (idempotent) YuzBilgi y0; if (!yuzBilgi(yuzOrdinal, y0, false)) return sonuc; // false → cache'i vur (düzlem alanı cache'te) if (!y0.duzlemMi) { sonuc.push_back(yuzOrdinal); return sonuc; } const f64 cosTol = std::cos(aciTolDeg * 3.14159265358979323846 / 180.0); for (u32 i = 1;; ++i) { YuzBilgi yk; if (!yuzBilgi(i, yk, false)) break; // yüz ordinali bitti (cache okuma) if (yk.duzlemMi && std::abs(yk.normal.nokta(y0.normal)) >= cosTol) sonuc.push_back(i); } return sonuc; } std::vector BrepGovde::benzerYuzler(u32 yuzOrdinal, f64 boyutTol) const { std::vector sonuc; if (!gecerliMi()) return sonuc; yuzBilgiOnbellekKur(); // tüm-yüz taraması → cache warm (idempotent; import worker zaten warm'lar) YuzBilgi y0; if (!yuzBilgi(yuzOrdinal, y0, false)) return sonuc; // false → cache'i vur (düzlem alanı cache'te) // Yüzey TİPİ + ÖLÇÜ eşleşen tüm yüzler (konum/oryantasyondan BAĞIMSIZ) → pattern-tabanlı // toplu düzenleme: "bu çaptaki tüm delikler" (silindir R eşi), "bu boyuttaki tüm yüzler" // (düzlem alan eşi), "bu yarıçaptaki küreler". boyutTol: yarıçapta MUTLAK (mm), alanda ORAN. const int tip0 = y0.silindirMi ? 1 : (y0.kureMi ? 2 : (y0.koniMi ? 3 : (y0.duzlemMi ? 0 : -1))); if (tip0 < 0) { sonuc.push_back(yuzOrdinal); return sonuc; } // serbest yüzey → yalnız kendisi for (u32 i = 1;; ++i) { YuzBilgi yk; if (!yuzBilgi(i, yk, false)) break; // cache okuma const int tip = yk.silindirMi ? 1 : (yk.kureMi ? 2 : (yk.koniMi ? 3 : (yk.duzlemMi ? 0 : -1))); if (tip != tip0) continue; bool eş = false; if (tip0 == 0) // düzlem: alan ORAN toleransı (aynı boyut yüz) eş = std::abs(yk.alan - y0.alan) <= boyutTol * std::max(y0.alan, 1e-9); else // silindir/küre/koni: yarıçap MUTLAK toleransı (aynı çap) eş = std::abs(yk.yaricap - y0.yaricap) <= std::max(boyutTol, 1e-4); if (eş) sonuc.push_back(i); } return sonuc; } std::vector BrepGovde::benzerKenarlar(u32 kenarOrdinal, f64 boyutTol) const { std::vector sonuc; if (!gecerliMi()) return sonuc; KenarBilgi k0; if (!kenarBilgi(kenarOrdinal, k0)) return sonuc; // Kenar TİPİ + ÖLÇÜ eşleşen tüm kenarlar (konum/oryantasyon BAĞIMSIZ) → pattern seçimi: // "bu R'deki tüm çember kenarları" (fillet zinciri/delik ağzı — yaricap MUTLAK tol) veya // "bu uzunluktaki tüm doğru kenarlar" (uzunluk MUTLAK tol). benzerYuzler'in kenar ikizi. const bool cember0 = k0.cemberMi; const bool dogru0 = k0.dogruMu; if (!cember0 && !dogru0) { sonuc.push_back(kenarOrdinal); return sonuc; } // serbest eğri → kendisi const f64 tol = std::max(boyutTol, 1e-4); for (u32 i = 1;; ++i) { KenarBilgi kk; if (!kenarBilgi(i, kk)) break; bool eş = false; if (cember0 && kk.cemberMi) eş = std::abs(kk.yaricap - k0.yaricap) <= tol; // aynı R çember else if (dogru0 && kk.dogruMu) eş = std::abs(kk.uzunluk - k0.uzunluk) <= tol; // aynı boy doğru if (eş) sonuc.push_back(i); } return sonuc; } NoktaVeri BrepGovde::noktaMesh() const { NoktaVeri sonuc; if (!gecerliMi()) return sonuc; try { // Ayni koordinattaki koseleri tekrar saymamak icin ordinal biriktir u32 noktaOrdinal = 0; for (TopExp_Explorer kesif(icerik_->sekil, TopAbs_VERTEX); kesif.More(); kesif.Next()) { const TopoDS_Vertex kose = TopoDS::Vertex(kesif.Current()); const gp_Pnt p = BRep_Tool::Pnt(kose); ++noktaOrdinal; sonuc.vertexler.push_back({{(f32)p.X(), (f32)p.Y(), (f32)p.Z()}, noktaOrdinal}); } sonuc.noktaSayi = noktaOrdinal; } catch (const Standard_Failure& hata) { gunluk::hata("Nokta cikarma hatasi: {}", hata.what()); } return sonuc; } BrepGovde kutu(f64 g, f64 d, f64 y) { try { gp_Trsf ortala; ortala.SetTranslation(gp_Vec(-g / 2, -d / 2, -y / 2)); return sar(BRepBuilderAPI_Transform(BRepPrimAPI_MakeBox(g, d, y).Shape(), ortala).Shape()); } catch (const Standard_Failure&) { return {}; } } BrepGovde silindir(f64 r, f64 y) { try { gp_Trsf ortala; ortala.SetTranslation(gp_Vec(0, 0, -y / 2)); return sar(BRepBuilderAPI_Transform(BRepPrimAPI_MakeCylinder(r, y).Shape(), ortala).Shape()); } catch (const Standard_Failure&) { return {}; } } BrepGovde kure(f64 r) { try { return sar(BRepPrimAPI_MakeSphere(r).Shape()); } catch (const Standard_Failure&) { return {}; } } BrepGovde koni(f64 r, f64 y) { try { gp_Trsf ortala; ortala.SetTranslation(gp_Vec(0, 0, -y / 2)); return sar(BRepBuilderAPI_Transform(BRepPrimAPI_MakeCone(r, 0.0, y).Shape(), ortala).Shape()); } catch (const Standard_Failure&) { return {}; } } namespace { // op: islem tur ayirtici (0 fuse, 1 cut, 2 common) — imzaya girer. template BrepGovde boolIslem(const BrepGovde& a, const BrepGovde& b, const char* ad, u64 op) { if (!a.gecerliMi() || !b.gecerliMi()) return {}; static thread_local ic::SonucOnbellek onbellek; const std::vector girdi{sekil(a), sekil(b)}; const u64 imza = ic::imzaKat(ic::imzaKat(1469598103934665603ull, op), (u64)(a.yuzRenkliMi() ? 1u : 0u) ^ ((u64)(b.yuzRenkliMi() ? 2u : 0u))); if (const ic::SonucGiris* c = onbellek.bul(girdi, imza)) { BrepGovde s = sar(c->sonuc); if (c->renkli) s.yuzRenkleriAta(c->renkler); return s; } try { Islem islem(sekil(a), sekil(b)); islem.SetRunParallel(Standard_False); // OSD_ThreadPool bypass: nested mesh/boolean deadlock önle islem.Build(); if (!islem.IsDone()) { gunluk::uyari("Boolean {} basarisiz", ad); return {}; } BrepGovde sonuc = sar(islem.Shape()); // Per-yuz renkleri her iki operanddan tasi (a→b sirasi; ayni algo). ic::renkPropagasyonu(a, islem, sonuc); ic::renkPropagasyonu(b, islem, sonuc); onbellek.ekle(girdi, imza, sekil(sonuc), sonuc.yuzRenkleri(), sonuc.yuzRenkliMi()); return sonuc; } catch (const Standard_Failure& hata) { gunluk::hata("Boolean {} hatasi: {}", ad, hata.what()); return {}; } } } void occtSinyalKur() { // theFloatingSignal=false: access violation / illegal instruction'ı Standard_Failure'a // çevir (FP division-by-zero'yu TRAP etme → OCCT içi FP kullanımı bozulmasın). OSD::SetSignal(Standard_False); } BrepGovde birlesim(const BrepGovde& a, const BrepGovde& b) { return boolIslem(a, b, "birlesim", 0); } BrepGovde cikarma(const BrepGovde& a, const BrepGovde& b) { return boolIslem(a, b, "cikarma", 1); } BrepGovde kesisim(const BrepGovde& a, const BrepGovde& b) { return boolIslem(a, b, "kesisim", 2); } BrepGovde donusumUygula(const BrepGovde& g, const Donusumd& d) { if (!g.gecerliMi()) return {}; // Birim (identity) donusum: OCCT BRepBuilderAPI_Transform yine de derin-kopya uretir. // Konum ~0, yonelim ~birim quaternion, olcek ~1 ise sekli PAYLAS (TopoDS_Shape handle // kopyasi; TShape refcount) → gereksiz kopya/yeni TShape yok. Part Design zincirinde // hedefDunyaBrep her recompute'ta identity donusum uygular; bu, boolean girdilerinin // kimligini (TShape) da stabil tutar. const bool birimKonum = std::abs(d.konum.x) < 1e-12 && std::abs(d.konum.y) < 1e-12 && std::abs(d.konum.z) < 1e-12; const bool birimOlcek = std::abs(d.olcek.x - 1.0) < 1e-12; const bool birimYon = std::abs(d.yonelim.x) < 1e-12 && std::abs(d.yonelim.y) < 1e-12 && std::abs(d.yonelim.z) < 1e-12 && std::abs(std::abs(d.yonelim.w) - 1.0) < 1e-12; if (birimKonum && birimOlcek && birimYon) { BrepGovde sonuc = sar(sekil(g)); // TShape paylasilir, kopya yok if (g.yuzRenkliMi()) sonuc.yuzRenkleriAta(g.yuzRenkleri()); return sonuc; } try { gp_Trsf t; t.SetRotation(gp_Quaternion(d.yonelim.x, d.yonelim.y, d.yonelim.z, d.yonelim.w)); t.SetTranslationPart(gp_Vec(d.konum.x, d.konum.y, d.konum.z)); if (std::abs(d.olcek.x - 1.0) > 1e-12) t.SetScaleFactor(d.olcek.x); BrepGovde sonuc = sar(BRepBuilderAPI_Transform(sekil(g), t, true).Shape()); // Rijit/olcekli donusum yuz TopExp sirasini korur → per-yuz renkleri AYNEN kopyala. if (g.yuzRenkliMi()) sonuc.yuzRenkleriAta(g.yuzRenkleri()); return sonuc; } catch (const Standard_Failure&) { return {}; } } } // namespace fiko::geometri