#include "brep_ic.hpp" #include #include #include #include #include #include "fiko/geometri/kalite.hpp" // filletDenemeMs — köşe-clamp zaman kutusu (AyarSistemi) #include #include #include #include #include #include #include #include // Yüz Kaldır (CATIA Remove Face): yüz sil + komşu uzat #include // Yüz Sil (Delete Face): yüzü çıkar → açık shell #include // Yüz Kopyala: shell inşa (GSD açık yüzey gövdesi) #include #include #include // kopya bağımsız (kaynak geometriyle paylaşımsız) #include // Kabuktan Katı: yüzleri dik (tolerans) → kapalı shell #include // kapalı shell → katı #include // emboss boss: koincident taban yüzü glue-fuse #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 // sketch-on-surface emboss (yuzeKabart) #include #include #include #include #include // fillet/pah sonucu ince self-kesişim (köşe artefaktı) doğrulama #include #include #include // tekSolidCoz: compound tek-solid çözümü #include #include #include #include #include #include #include #include #include #include // noktaKalinlikYuklu: isin → GeomAdaptor_Curve #include #include // egriKonturWire: Spline gecis-nokta interpolasyonu #include #include // undercutRapor: UV ornegi trim ICINDE mi #include #include #include #include #include // küre push/pull (kureRadyalOfset) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Sketch-on-Surface (WRAP): pcurve (Geom2d) + yüzey → BREP-exact sarılı edge. #include #include #include #include // emboss boss: profili içe kaydırılmış yüzeyde kur (örtüşme) #include #include #include #include #include #include #include #include #include "fiko/core/gunluk.hpp" namespace fiko::geometri { using ic::sar; using ic::sekil; // ---- 3B Pah / Kose Yuvarlatma (BRepFilletAPI) -------------------------- // Ordinal-tabanli kenar secimi: kenarMesh/kenarBilgi ile AYNI TopExp sirasi. namespace { // TEK-SOLID compound çözücü: boolean (Fuse/Cut) sonuçları TopoDS_Compound sarılı gelir; // OCCT local-op'ları (BRepFeat_MakePrism, MakeFillet/MakeChamfer) compound TABANDA // başarısız olur → PartDesign feature-gövdelerinde push/pull ve fillet/pah hiç // çalışmıyordu. Compound'un TEK üst-düzey çocuğu varsa ve solid'e iniyorsa onu döner // (iç içe compound'lar da çözülür); aksi halde şekli AYNEN döner. Ordinaller korunur: // TopExp yüz/kenar sırası tek-çocuklu compound'da içteki solid ile birebir aynıdır. TopoDS_Shape tekSolidCoz(const TopoDS_Shape& s) { TopoDS_Shape su = s; while (!su.IsNull() && su.ShapeType() == TopAbs_COMPOUND) { TopoDS_Iterator it(su); if (!it.More()) return s; // boş compound → dokunma const TopoDS_Shape ilk = it.Value(); it.Next(); if (it.More()) return s; // birden çok çocuk → ordinal riski, dokunma su = ilk; } return (!su.IsNull() && su.ShapeType() == TopAbs_SOLID) ? su : s; } // Verilen ordinallerdeki kenarlari (1-tabanli) TopExp sirasinda toplar. std::vector ordinalKenarlar(const TopoDS_Shape& s, const std::vector& ord) { std::vector cikti; if (ord.empty()) return cikti; u32 n = 0; for (TopExp_Explorer k(s, TopAbs_EDGE); k.More(); k.Next()) { ++n; if (std::find(ord.begin(), ord.end(), n) != ord.end()) cikti.push_back(TopoDS::Edge(k.Current())); } return cikti; } // ordinalKenarlar'in YÜZ ikizi: 1-tabanli yüz ordinalleri → TopoDS_Face (TopExp FACE // sirasi; kabuk/draft yüz secimi kenarBilgi/pick ile ayni sirayi kullanir). std::vector ordinalYuzler(const TopoDS_Shape& s, const std::vector& ord) { std::vector cikti; if (ord.empty()) return cikti; u32 n = 0; for (TopExp_Explorer y(s, TopAbs_FACE); y.More(); y.Next()) { ++n; if (std::find(ord.begin(), ord.end(), n) != ord.end()) cikti.push_back(TopoDS::Face(y.Current())); } return cikti; } // Tek 1-tabanli ordinaldeki kenar/yüz (bulunmazsa null döner — IsNull() ile kontrol). TopoDS_Edge ordinalKenar(const TopoDS_Shape& s, u32 ord) { std::vector t{ord}; auto v = ordinalKenarlar(s, t); return v.empty() ? TopoDS_Edge() : v.front(); } TopoDS_Face ordinalYuz(const TopoDS_Shape& s, u32 ord) { std::vector t{ord}; auto v = ordinalYuzler(s, t); return v.empty() ? TopoDS_Face() : v.front(); } // İki yüzün PAYLAŞTIĞI kenarlar (Face-Face / Tritangent MVP çekirdeği): edge→faces // ata haritasıyla, ancestor listesinde HER İKİ yüzü de barındıran kenarları toplar. std::vector ortakKenarlar(const TopoDS_Shape& s, const TopoDS_Face& y1, const TopoDS_Face& y2) { std::vector cikti; if (y1.IsNull() || y2.IsNull()) return cikti; TopTools_IndexedDataMapOfShapeListOfShape harita; TopExp::MapShapesAndAncestors(s, TopAbs_EDGE, TopAbs_FACE, harita); for (int i = 1; i <= harita.Extent(); ++i) { bool y1var = false, y2var = false; for (const TopoDS_Shape& yz : harita.FindFromIndex(i)) { // NCollection_List range-for if (yz.IsSame(y1)) y1var = true; if (yz.IsSame(y2)) y2var = true; } if (y1var && y2var) cikti.push_back(TopoDS::Edge(harita.FindKey(i))); } return cikti; } } // namespace // pah/yuvarlat imzasi: op (0 pah,1 yuvarlat) + kenar ordinalleri + skaler (mesafe/yaricap). static u64 filletImza(u64 op, const std::vector& kenarlar, f64 skaler) { u64 h = ic::imzaKat(1469598103934665603ull, op); for (u32 k : kenarlar) h = ic::imzaKat(h, (u64)k); return ic::imzaKat(h, skaler); } // Girdi BRepCheck sonucu önbelleği (fillet/pah girdi-geçerlilik kontrolü kirli devasa gövdede // ~2sn): aynı TShape için bir kez hesaplanır. Shape SAKLANIR (refcount canlı → ABA yok). // GLOBAL + mutex (thread_local DEĞİL): kaba-mesh worker'ı import sonrası ÖN-ISITIR → // ilk fillet main-thread'de beklemez. Analiz kilit DIŞINDA koşar (uzun; kilit kısa tutulur). static bool govdeGecerliOnbellekli(const TopoDS_Shape& s) { static std::mutex kilit; static std::vector> onb; { std::lock_guard g(kilit); for (const auto& [sh, v] : onb) if (sh.IsSame(s)) return v; } const bool v = BRepCheck_Analyzer(s).IsValid(); std::lock_guard g(kilit); for (const auto& [sh, mv] : onb) if (sh.IsSame(s)) return mv; // yarışta ilk yazan kazandı if (onb.size() >= 8) onb.erase(onb.begin()); onb.emplace_back(s, v); return v; } bool govdeSaglikliOnbellekli(const BrepGovde& g) { if (!g.gecerliMi()) return false; const TopoDS_Shape s = sekil(g); // Mesh-temelli (yüzeysiz VRML/STL): BRepCheck yüzey ister → 4000+ yüzde anlamsız + // çok yavaş (donma). Görüntü mesh'i boolean/feature'a girmez → sağlıklı say (hızlı). if (TopExp_Explorer k(s, TopAbs_FACE); k.More() && BRep_Tool::Surface(TopoDS::Face(k.Current())).IsNull()) return true; return govdeGecerliOnbellekli(tekSolidCoz(s)); } // KESİN yüz geri-haritası: yeni şekildeki her yüz için girdi ordinali (0 = işlem yarattı). // OCCT MakeShape history'si (IsDeleted / Modified / değişmemiş aynı-TShape) — feature // provenance'ın çekirdeği; geometrik tahmin değil BREP topolojisi (remap[j] = yeni (j+1). // yüzün eski ordinali). Alt-şekil sorgusu atarsa o yüz yeni sayılır (güvenli taraf). static void yuzGeriHaritaKur(const TopoDS_Shape& eskiS, const TopoDS_Shape& yeniS, BRepBuilderAPI_MakeShape& islem, std::vector& remap) { TopTools_IndexedMapOfShape yeniIdx; for (TopExp_Explorer k(yeniS, TopAbs_FACE); k.More(); k.Next()) yeniIdx.Add(k.Current()); remap.assign(yeniIdx.Extent(), 0); u32 o = 0; for (TopExp_Explorer e(eskiS, TopAbs_FACE); e.More(); e.Next()) { ++o; const TopoDS_Shape& f = e.Current(); const auto ata = [&](const TopoDS_Shape& yf) { const int i = yeniIdx.FindIndex(yf); if (i >= 1 && (size_t)i <= remap.size() && remap[i - 1] == 0) remap[i - 1] = (int)o; }; try { if (islem.IsDeleted(f)) continue; const TopTools_ListOfShape& mod = islem.Modified(f); if (mod.IsEmpty()) ata(f); else for (const TopoDS_Shape& yf : mod) ata(yf); } catch (const Standard_Failure&) { continue; } } } BrepGovde pahla(const BrepGovde& g, const std::vector& kenarlar, f64 mesafe, std::vector* yuzRemap) { if (yuzRemap) yuzRemap->clear(); if (!g.gecerliMi() || kenarlar.empty() || mesafe <= 1e-9) return {}; static thread_local ic::SonucOnbellek onbellek; const std::vector girdi{sekil(g)}; const u64 imza = filletImza(0, kenarlar, mesafe); if (const ic::SonucGiris* c = onbellek.bul(girdi, imza)) { BrepGovde s = sar(c->sonuc); if (c->renkli) s.yuzRenkleriAta(c->renkler); if (yuzRemap) *yuzRemap = c->yuzRemap; return s; } try { const TopoDS_Shape s = tekSolidCoz(sekil(g)); // compound sarili boolean sonucu (PartDesign) desteklenir const auto edges = ordinalKenarlar(s, kenarlar); // GİRDİ geçerliliği (lazy, en çok bir kez): kirli STEP gövdesi ZATEN BRepCheck'te geçersiz // olabilir — o zaman SONUCUN analiz başarısızlığı pahın suçu değildir, IsDone+hacim yeterli // sayılır (aksi halde her mesafe "geçersiz şekil" diye reddediliyordu). Girdi geçerliyken // geçersiz sonuç = gerçek köşe artefaktı → küçültme denemesi sürer (davranış korunur). int girdiGecerli = -1; const auto girdiGecerliMi = [&] { if (girdiGecerli < 0) { girdiGecerli = govdeGecerliOnbellekli(s) ? 1 : 0; if (!girdiGecerli) gunluk::uyari("3B pah: girdi gövde BRepCheck'te geçersiz (kirli STEP) — sonuç analizi atlanacak"); } return girdiGecerli == 1; }; // Köşe/kesişim robustluğu (yuvarlat ile aynı): mesafe sığmazsa kademeli küçült + yeniden dene. // ZAMAN KUTUSU: deneme filletDenemeMs'i aşıp başarısızsa kalanlar atlanır (devasa gövdede // 6 tam deneme dakikalar sürüyordu); her başarısız deneme süre+nedenle loglanır. for (int deneme = 0; deneme < 6; ++deneme) { const f64 m = mesafe * std::pow(0.72, deneme); if (m < 1e-4) break; const auto dt0 = std::chrono::steady_clock::now(); const auto gecenMs = [&] { return std::chrono::duration(std::chrono::steady_clock::now() - dt0) .count(); }; const auto denemeLog = [&](const char* neden) { gunluk::uyari("3B pah deneme {}: m={:.2f} {} ({:.0f} ms)", deneme + 1, m, neden, gecenMs()); return gecenMs() > filletDenemeMs(); // true → kalan denemeleri atla }; try { BRepFilletAPI_MakeChamfer islem(s); for (const TopoDS_Edge& k : edges) islem.Add(m, k); islem.Build(); if (!islem.IsDone()) { if (denemeLog("başarısız")) break; continue; } if (!BRepCheck_Analyzer(islem.Shape()).IsValid() && girdiGecerliMi()) { // ince self-kesişim (köşe) → küçült if (denemeLog("geçersiz şekil")) break; continue; } BrepGovde sonuc = sar(islem.Shape()); if (!sonuc.doluMu()) { if (denemeLog("boş sonuç")) break; continue; } ic::renkPropagasyonu(g, islem, sonuc); // per-yuz renkleri koru (pah yuzeyi komsudan) std::vector remap; // provenance: değişmeyen/Modified yüzler eski ordinaline, pah yüzleri 0 yuzGeriHaritaKur(s, sekil(sonuc), islem, remap); if (yuzRemap) *yuzRemap = remap; onbellek.ekle(girdi, imza, sekil(sonuc), sonuc.yuzRenkleri(), sonuc.yuzRenkliMi(), std::move(remap)); if (deneme > 0) gunluk::uyari("3B pah: mesafe={:.2f} sığmadı → {:.2f} uygulandı (köşe clamp)", mesafe, m); return sonuc; } catch (const Standard_Failure& hd) { if (denemeLog(hd.GetMessageString() ? hd.GetMessageString() : "istisna")) break; continue; } } gunluk::uyari("3B pah: hiçbir mesafe sığmadı (köşe/kesişim)"); return {}; } catch (const Standard_Failure& h) { gunluk::hata("3B pah hatasi: {}", h.what()); return {}; } } BrepGovde yuvarlat(const BrepGovde& g, const std::vector& kenarlar, f64 yaricap, std::vector* yuzRemap) { if (yuzRemap) yuzRemap->clear(); if (!g.gecerliMi() || kenarlar.empty() || yaricap <= 1e-9) return {}; static thread_local ic::SonucOnbellek onbellek; const std::vector girdi{sekil(g)}; const u64 imza = filletImza(1, kenarlar, yaricap); if (const ic::SonucGiris* c = onbellek.bul(girdi, imza)) { BrepGovde s = sar(c->sonuc); if (c->renkli) s.yuzRenkleriAta(c->renkler); if (yuzRemap) *yuzRemap = c->yuzRemap; return s; } try { const TopoDS_Shape s = tekSolidCoz(sekil(g)); // compound sarili boolean sonucu (PartDesign) desteklenir const auto edges = ordinalKenarlar(s, kenarlar); // KÖŞE/KESİŞİM ROBUSTLUĞU (kullanıcı: köşe radiüs hataları büyütmede): R çok büyükse (komşu // fillet/yüz sığmaz) Build başarısız/çöker/geçersiz → R'yi kademeli küçült (×0.72) + yeniden // dene → köşede EN BÜYÜK olası yarıçapa CLAMP (CATIA/Fusion gibi; tamamen başarısız değil). // GİRDİ geçerliliği (lazy — pah ile aynı gerekçe): kirli STEP'te sonuç analizi atlanır. int girdiGecerli = -1; const auto girdiGecerliMi = [&] { if (girdiGecerli < 0) { girdiGecerli = govdeGecerliOnbellekli(s) ? 1 : 0; if (!girdiGecerli) gunluk::uyari("3B yuvarlatma: girdi gövde BRepCheck'te geçersiz (kirli STEP) — sonuç analizi atlanacak"); } return girdiGecerli == 1; }; // ZAMAN KUTUSU: deneme filletDenemeMs'i aşıp başarısızsa kalanlar atlanır (devasa gövdede // 6 tam deneme dakikalar sürüyordu); her başarısız deneme süre+nedenle loglanır. for (int deneme = 0; deneme < 6; ++deneme) { const f64 r = yaricap * std::pow(0.72, deneme); if (r < 1e-4) break; const auto dt0 = std::chrono::steady_clock::now(); const auto gecenMs = [&] { return std::chrono::duration(std::chrono::steady_clock::now() - dt0) .count(); }; const auto denemeLog = [&](const char* neden) { gunluk::uyari("3B yuvarlatma deneme {}: R={:.2f} {} ({:.0f} ms)", deneme + 1, r, neden, gecenMs()); return gecenMs() > filletDenemeMs(); // true → kalan denemeleri atla }; try { BRepFilletAPI_MakeFillet islem(s); for (const TopoDS_Edge& k : edges) islem.Add(r, k); islem.Build(); if (!islem.IsDone()) { if (denemeLog("başarısız")) break; continue; } // bu R sığmadı → küçült if (!BRepCheck_Analyzer(islem.Shape()).IsValid() && girdiGecerliMi()) { // ince self-kesişim → küçült if (denemeLog("geçersiz şekil")) break; continue; } BrepGovde sonuc = sar(islem.Shape()); if (!sonuc.doluMu()) { if (denemeLog("boş sonuç")) break; continue; } // geçersiz → küçült ic::renkPropagasyonu(g, islem, sonuc); // per-yuz renkleri koru (yuvarlatma yuzeyi komsudan) std::vector remap; // provenance: değişmeyen/Modified yüzler eski ordinaline, bevel 0 yuzGeriHaritaKur(s, sekil(sonuc), islem, remap); if (yuzRemap) *yuzRemap = remap; onbellek.ekle(girdi, imza, sekil(sonuc), sonuc.yuzRenkleri(), sonuc.yuzRenkliMi(), std::move(remap)); if (deneme > 0) gunluk::uyari("3B yuvarlatma: R={:.2f} sığmadı → R={:.2f} uygulandı (köşe clamp)", yaricap, r); return sonuc; } catch (const Standard_Failure& hd) { // bu R çöktü → küçült (zaman kutusu dahil) if (denemeLog(hd.GetMessageString() ? hd.GetMessageString() : "istisna")) break; continue; } } gunluk::uyari("3B yuvarlatma: hiçbir yarıçap sığmadı (köşe/kesişim)"); return {}; } catch (const Standard_Failure& h) { gunluk::hata("3B yuvarlatma hatasi: {}", h.what()); return {}; } } // ---- Variable Radius Fillet: tek kenara başı r1 / sonu r2 değişken yay ---- BrepGovde yuvarlatDegisken(const BrepGovde& g, u32 kenar, f64 r1, f64 r2, bool kubik) { if (!g.gecerliMi() || kenar == 0 || r1 <= 1e-9 || r2 <= 1e-9) return {}; try { const TopoDS_Edge k = ordinalKenar(tekSolidCoz(sekil(g)), kenar); if (k.IsNull()) { gunluk::uyari("Değişken fillet: kenar eşleşmedi"); return {}; } BRepFilletAPI_MakeFillet islem(tekSolidCoz(sekil(g))); // Add(r1, r2, edge): kenar başında r1, sonunda r2 — linear yasa. (kubik parametresi // ileride ChFi3d law-evolution ile eğri yumuşatma için; MVP linear iki-nokta.) islem.Add(r1, r2, k); (void)kubik; islem.Build(); if (!islem.IsDone()) { gunluk::uyari("Değişken fillet başarısız"); return {}; } BrepGovde sonuc = sar(islem.Shape()); ic::renkPropagasyonu(g, islem, sonuc); return sonuc; } catch (const Standard_Failure& h) { gunluk::hata("Değişken fillet hatasi: {}", h.what()); return {}; } } // ---- Face-Face Fillet: iki yüz arası (ortak kenar üzerinden) fillet ---- BrepGovde yuvarlatYuzYuz(const BrepGovde& g, u32 yuz1, u32 yuz2, f64 yaricap) { if (!g.gecerliMi() || yuz1 == 0 || yuz2 == 0 || yuz1 == yuz2 || yaricap <= 1e-9) return {}; try { const TopoDS_Face y1 = ordinalYuz(sekil(g), yuz1); const TopoDS_Face y2 = ordinalYuz(sekil(g), yuz2); const std::vector ortak = ortakKenarlar(sekil(g), y1, y2); if (ortak.empty()) { gunluk::uyari("Yüz-Yüz fillet: iki yüz ortak kenar paylaşmıyor (ayrık yüz TODO)"); return {}; } BRepFilletAPI_MakeFillet islem(tekSolidCoz(sekil(g))); for (const TopoDS_Edge& k : ortak) islem.Add(yaricap, k); islem.Build(); if (!islem.IsDone()) { gunluk::uyari("Yüz-Yüz fillet başarısız"); return {}; } BrepGovde sonuc = sar(islem.Shape()); ic::renkPropagasyonu(g, islem, sonuc); return sonuc; } catch (const Standard_Failure& h) { gunluk::hata("Yüz-Yüz fillet hatasi: {}", h.what()); return {}; } } // ---- Tritangent Fillet: 2 tut + 1 kaldır yüz → R otomatik (MVP yaklaşım) ---- BrepGovde yuvarlatTritangent(const BrepGovde& g, u32 tutYuz1, u32 tutYuz2, u32 kaldirYuz) { if (!g.gecerliMi() || tutYuz1 == 0 || tutYuz2 == 0 || kaldirYuz == 0) return {}; if (tutYuz1 == tutYuz2 || tutYuz1 == kaldirYuz || tutYuz2 == kaldirYuz) return {}; try { const TopoDS_Face t1 = ordinalYuz(sekil(g), tutYuz1); const TopoDS_Face t2 = ordinalYuz(sekil(g), tutYuz2); const TopoDS_Face kald = ordinalYuz(sekil(g), kaldirYuz); if (t1.IsNull() || t2.IsNull() || kald.IsNull()) { gunluk::uyari("Tritangent: yüz eşleşmedi"); return {}; } const std::vector ortak = ortakKenarlar(sekil(g), t1, t2); if (ortak.empty()) { gunluk::uyari("Tritangent: iki tutulan yüz ortak kenar paylaşmıyor"); return {}; } // R otomatik: ortak kenar ile kaldırılacak yüz arası minimum mesafe (üç yüze teğet // yaklaşık = köşe ile karşı yüz arası boşluk). TODO: gerçek ChFi3d tri-tangent solver. f64 r = 0.0; for (const TopoDS_Edge& k : ortak) { BRepExtrema_DistShapeShape d(k, kald); if (d.IsDone() && d.Value() > r) r = d.Value(); } if (r <= 1e-6) { gunluk::uyari("Tritangent: yarıçap türetilemedi"); return {}; } BRepFilletAPI_MakeFillet islem(tekSolidCoz(sekil(g))); for (const TopoDS_Edge& k : ortak) islem.Add(r, k); islem.Build(); if (!islem.IsDone()) { gunluk::uyari("Tritangent fillet başarısız"); return {}; } BrepGovde sonuc = sar(islem.Shape()); ic::renkPropagasyonu(g, islem, sonuc); gunluk::bilgi("Tritangent fillet: otomatik yarıçap ≈ {:.3f} mm", r); return sonuc; } catch (const Standard_Failure& h) { gunluk::hata("Tritangent fillet hatasi: {}", h.what()); return {}; } } // ---- Kabuk (Shell / İçi Boşalt) ---------------------------------------- BrepGovde kabuk(const BrepGovde& g, const std::vector& yuzler, f64 kalinlik) { if (!g.gecerliMi() || yuzler.empty() || kalinlik <= 1e-9) return {}; try { TopTools_ListOfShape acikYuzler; // KALDIRILACAK (açık bırakılan) yüzler for (const TopoDS_Face& y : ordinalYuzler(sekil(g), yuzler)) acikYuzler.Append(y); if (acikYuzler.IsEmpty()) { gunluk::uyari("Kabuk: yüz eşleşmedi"); return {}; } // Kalinlik NEGATIF → içeri (dış boyut korunur, iç boşalır). Tolerans mm makul. BRepOffsetAPI_MakeThickSolid islem; islem.MakeThickSolidByJoin(sekil(g), acikYuzler, -std::abs(kalinlik), 1e-3); islem.Build(); if (!islem.IsDone()) { gunluk::uyari("Kabuk başarısız"); return {}; } BrepGovde sonuc = sar(islem.Shape()); ic::renkPropagasyonu(g, islem, sonuc); // per-yüz renkleri koru return sonuc; } catch (const Standard_Failure& h) { gunluk::hata("Kabuk hatası: {}", h.what()); return {}; } } // ---- Kalınlık (Thickness — katının TÜM yüzlerine uniform 3B offset) ---- // Kabuk'un aksine yüz KALDIRMAZ; sonuç içi dolu katı kalır. Pozitif → dışa büyür, // negatif → içe küçülür. PerformByJoin closed-solid'i offset'ler (Skin modu). BrepGovde kalinlastir(const BrepGovde& g, f64 mesafe) { if (!g.gecerliMi() || std::abs(mesafe) < 1e-9) return {}; try { BRepOffsetAPI_MakeOffsetShape islem; islem.PerformByJoin(sekil(g), mesafe, 1e-3, BRepOffset_Skin); islem.Build(); if (!islem.IsDone()) { gunluk::uyari("Kalınlık başarısız"); return {}; } BrepGovde sonuc = sar(islem.Shape()); ic::renkPropagasyonu(g, islem, sonuc); // per-yüz renkleri koru return sonuc; } catch (const Standard_Failure& h) { gunluk::hata("Kalınlık hatası: {}", h.what()); return {}; } } // ---- Yüz Sil (Delete Face) — açık shell bırak (onarma yok) ------------- BrepGovde yuzuSil(const BrepGovde& g, const std::vector& yuzler) { if (!g.gecerliMi() || yuzler.empty()) return {}; occtSinyalKur(); try { const TopoDS_Shape s = tekSolidCoz(sekil(g)); const auto yuzs = ordinalYuzler(s, yuzler); if (yuzs.empty()) { gunluk::uyari("Yüz sil: yüz eşleşmedi"); return {}; } BRepTools_ReShape rs; for (const TopoDS_Face& y : yuzs) rs.Remove(y); const TopoDS_Shape r = rs.Apply(s); if (r.IsNull()) { gunluk::uyari("Yüz sil: sonuç boş"); return {}; } // SOLID sarmalını AYIKLA → gerçek açık yüzey gövdesi (shell). Aksi halde yüz gitse de shape // hâlâ SOLID sarılı kalır → doluMu()=true (geometrik bozuk katı); kabuktanKati/Kalınlaştır // açık shell bekler. Tek shell → doğrudan; çok shell → compound (yuzKopyala kalıbı). TopoDS_Shape acik = r; { std::vector shells; for (TopExp_Explorer sh(r, TopAbs_SHELL); sh.More(); sh.Next()) shells.push_back(sh.Current()); if (shells.size() == 1) acik = shells.front(); else if (shells.size() > 1) { BRep_Builder bb; TopoDS_Compound comp; bb.MakeCompound(comp); for (const TopoDS_Shape& x : shells) bb.Add(comp, x); acik = comp; } } BrepGovde sonuc = sar(acik); if (!sonuc.gecerliMi()) { gunluk::uyari("Yüz sil: geçersiz sonuç"); return {}; } // Renk KORU: ReShape hayatta kalan yüz TShape'lerini + TopExp sırasını korur → silinen // ordinalleri sırayla ele → kalan yüzlere birebir renk taşınır (açık shell yüzey gövdesi). if (g.yuzRenkliMi()) { const auto& er = g.yuzRenkleri(); std::vector yr; u32 o = 0; for (TopExp_Explorer k(s, TopAbs_FACE); k.More(); k.Next()) { ++o; if (std::find(yuzler.begin(), yuzler.end(), o) != yuzler.end()) continue; // silinen yr.push_back(o <= er.size() ? er[o - 1] : Vec3f{0.62f, 0.66f, 0.72f}); } if (!yr.empty()) sonuc.yuzRenkleriAta(std::move(yr)); } return sonuc; } catch (const Standard_Failure& h) { gunluk::hata("Yüz sil hatası: {}", h.what()); return {}; } } // ---- Silindirik yüz Push/Pull (radyal offset — annulus boolean) -------- // Silindirik yüzün yarıçapını mesafe kadar radyal öteler. Orientation dış-normali // belirler (FORWARD=mil dışa, REVERSED=delik içe). mesafe>0 malzeme ekle, <0 çıkar. // Halka (annulus) = eş-eksenli iki silindir farkı; gövdeyle fuse/cut. Yüz eksen-boyu // verteks izdüşümünden alınır → komşu düz yüzlerle hizalı (artık yüz kalmaz). // UnifySameDomain: koplanar komşu yüz/kenarları TEK yüze birleştir (uzatılan yüzey ayrık // durmaz) + renkliAra'nın STEP renklerini usd History ile son şekle taşı. Boolean/silindir // dalları için (düz yüz dalı remap'i de usd gerektirdiğinden kendi kodunu tutar). Geçersizse boş. static void usdUcgenlemeTasi(const TopoDS_Shape& ara, const TopoDS_Shape& son, const Handle(BRepTools_History)& hist); // tanım yuzItCek üstünde static BrepGovde usdBirlestir(const BrepGovde& renkliAra, const TopoDS_Shape& araShape, const std::vector* araRemap = nullptr, std::vector* sonRemap = nullptr) { // UnifySameDomain koplanar/eş-domain yüzleri birleştirir (KOZMETİK). Başarısız/istisna → // HAM boolean sonucuna düş (silindir/küre/koni it-çek karmaşık gövdede aksi halde SESSİZCE // no-op oluyordu — kullanıcı: "önizleme güzel ama yapmıyor"). Geometri her hâlde uygulanır. // araRemap/sonRemap: provenance zinciri — araRemap (ara yüz → orijinal ordinal) usd // history'siyle son şekle taşınır (birleşen yüzlerde eski-ordinal öncelikli, yeni 0 kalır). // DEVASA GÖVDE EŞİĞİ: usd hem saniyeler sürüyor hem TÜM TShape'leri yeniden kurup // üçgenleme/kenar önbelleklerini boşa düşürüyordu (her it/çek = tam remesh). Eşik // üstünde kozmetik birleştirme atlanır → ham sonuç (geometri doğru, yüzler ayrık kalır). { u32 yuzSayi = 0; for (TopExp_Explorer k(araShape, TopAbs_FACE); k.More(); k.Next()) ++yuzSayi; if (yuzSayi > usdYuzEsigi()) { BrepGovde ham = sar(araShape); if (renkliAra.yuzRenkliMi()) ham.yuzRenkleriAta(renkliAra.yuzRenkleri()); if (araRemap && sonRemap) *sonRemap = *araRemap; return (ham.doluMu()) ? std::move(ham) : BrepGovde{}; } } try { ShapeUpgrade_UnifySameDomain usd(araShape, Standard_True, Standard_True, Standard_False); usd.Build(); BrepGovde sonuc = sar(usd.Shape()); if (sonuc.doluMu()) { usdUcgenlemeTasi(araShape, usd.Shape(), usd.History()); // 1:1 yüzlerde incremental mesh yaşasın const bool remapIste = araRemap && sonRemap; if (renkliAra.yuzRenkliMi() || remapIste) { Handle(BRepTools_History) h = usd.History(); TopTools_IndexedMapOfShape sonIdx; for (TopExp_Explorer k(usd.Shape(), TopAbs_FACE); k.More(); k.Next()) sonIdx.Add(k.Current()); const auto& ar = renkliAra.yuzRenkleri(); const bool renkli = renkliAra.yuzRenkliMi(); std::vector sr(sonIdx.Extent(), Vec3f{0.62f, 0.66f, 0.72f}); if (remapIste) sonRemap->assign(sonIdx.Extent(), 0); u32 ao = 0; for (TopExp_Explorer ak(araShape, TopAbs_FACE); ak.More(); ak.Next()) { ++ao; const Vec3f c = (renkli && ao <= ar.size()) ? ar[ao - 1] : Vec3f{0.62f, 0.66f, 0.72f}; const int eskiOrd = (remapIste && ao <= araRemap->size()) ? (*araRemap)[ao - 1] : 0; const TopoDS_Shape& af = ak.Current(); const auto boya = [&](const TopoDS_Shape& sf2) { const int i = sonIdx.FindIndex(sf2); if (i < 1 || (size_t)i > sr.size()) return; if (renkli) sr[i - 1] = c; if (remapIste && eskiOrd && (*sonRemap)[i - 1] == 0) (*sonRemap)[i - 1] = eskiOrd; }; if (h->IsRemoved(af)) continue; const TopTools_ListOfShape& mod = h->Modified(af); if (mod.IsEmpty()) boya(af); else for (const TopoDS_Shape& sf2 : mod) boya(sf2); } if (renkli) sonuc.yuzRenkleriAta(std::move(sr)); } return sonuc; } gunluk::uyari("Birleştirme geçersiz — ham boolean sonucu kullanılıyor"); } catch (const Standard_Failure& e) { gunluk::uyari("Birleştirme istisnası ({}) — ham boolean sonucu kullanılıyor", e.what()); } BrepGovde ham = sar(araShape); // fallback: birleştirmesiz ham geometri (radius değişimi uygulanır) if (araRemap && sonRemap) *sonRemap = *araRemap; // usd yok → ara haritası birebir geçerli return (ham.doluMu()) ? std::move(ham) : BrepGovde{}; } // Silindir parametresi: canonical GeomAbs_Cylinder VEYA sketch'ten extrude edilen daire // (GeomAbs_SurfaceOfExtrusion, taban eğri çember). İkincisi push/pull için silindir sayılır. static bool silindirParam(const BRepAdaptor_Surface& sf, gp_Pnt& loc, gp_Dir& dir, f64& R) { if (sf.GetType() == GeomAbs_Cylinder) { const gp_Cylinder cyl = sf.Cylinder(); loc = cyl.Axis().Location(); dir = cyl.Axis().Direction(); R = cyl.Radius(); return true; } if (sf.GetType() == GeomAbs_SurfaceOfExtrusion) { Handle(Adaptor3d_Curve) bc = sf.BasisCurve(); if (!bc.IsNull() && bc->GetType() == GeomAbs_Circle) { const gp_Circ cc = bc->Circle(); loc = cc.Location(); dir = sf.Direction(); R = cc.Radius(); return true; } } return false; } static BrepGovde silindirRadyalOfset(const BrepGovde& g, const TopoDS_Shape& s, const TopoDS_Face& yuz, const gp_Pnt& loc, const gp_Dir& dir, f64 R, f64 mesafe, std::vector* yuzRemap = nullptr) { const gp_Vec dv(dir); // delik: yüzey NORMALİNİN (orientation dahil) radyal-dış yönle ilişkisi. Dış duvar → dışa radyal // (delik=false), iç delik → içe (true). Ham orientation güvenilmez: sketch'ten extrude edilen // dairenin dış duvarı REVERSED olabiliyor → annulus gövde İÇİNE kurulup fuse no-op oluyordu. bool delik = (yuz.Orientation() == TopAbs_REVERSED); { BRepAdaptor_Surface sfd(yuz); const f64 um = 0.5 * (sfd.FirstUParameter() + sfd.LastUParameter()); const f64 vm = 0.5 * (sfd.FirstVParameter() + sfd.LastVParameter()); gp_Pnt P; gp_Vec dU, dV; sfd.D1(um, vm, P, dU, dV); gp_Vec nrm = dU.Crossed(dV); if (nrm.Magnitude() > 1e-12) { if (yuz.Orientation() == TopAbs_REVERSED) nrm.Reverse(); // dış normal const gp_Vec rel(loc, P); const gp_Vec radv = rel - dv * rel.Dot(dv); // radyal-dış bileşen if (radv.Magnitude() > 1e-9) delik = (nrm.Dot(radv) < 0.0); // normal içe → delik } } const f64 Rn = delik ? (R - mesafe) : (R + mesafe); if (Rn < 1e-2) { gunluk::uyari("Silindir it/çek: yarıçap çok küçük"); return {}; } // Eksen boyu: yalnız köşe vertexleri DEĞİL, sınır KENARLARINI örnekle → açılı/eğri tabanlı // boss'ta (elips kesit) gerçek eksen aralığı yakalanır (annulus tüm gövdeyi kaplar). f64 tmin = 1e300, tmax = -1e300; for (TopExp_Explorer e(yuz, TopAbs_EDGE); e.More(); e.Next()) { BRepAdaptor_Curve c(TopoDS::Edge(e.Current())); const f64 u0 = c.FirstParameter(), u1 = c.LastParameter(); for (int i = 0; i <= 16; ++i) { const gp_Pnt p = c.Value(u0 + (u1 - u0) * (f64)i / 16.0); const f64 t = gp_Vec(loc, p).Dot(dv); if (t < tmin) tmin = t; if (t > tmax) tmax = t; } } if (tmax - tmin < 1e-4) { gunluk::uyari("Silindir it/çek: eksen boyu belirsiz"); return {}; } const f64 fuzzy = std::max(R * 1e-4, 1e-5); // boolean tolerans (karmaşık gövde sağlamlığı) const f64 Rin = std::min(R, Rn), Rout = std::max(R, Rn), boy = tmax - tmin; const gp_Ax2 ax2(loc.Translated(dv * tmin), dir); const TopoDS_Shape disS = BRepPrimAPI_MakeCylinder(ax2, Rout, boy).Shape(); const TopoDS_Shape icS = BRepPrimAPI_MakeCylinder(ax2, Rin, boy).Shape(); const TopoDS_Shape halka = BRepAlgoAPI_Cut(disS, icS).Shape(); // Ara boolean → STEP renkleri (renkPropagasyonu) → UnifySameDomain birleştir + renk taşı. // Böylece silindir push/pull sonrası da uzatılan/komşu yüzler bütünleşir (düz yüz gibi). // Provenance: boolean history (s→ara) + usd history (ara→son) KESİN zincir — Modified // (uzatılan) yüzler eski ordinalinde kalır, yalnız gerçekten yeni yüzler 0. const auto tamamla = [&](const TopoDS_Shape& araShape, auto& op) -> BrepGovde { BrepGovde araBrep = sar(araShape); if (!araBrep.doluMu()) { gunluk::uyari("Silindir it/çek: geçersiz sonuç"); return {}; } ic::renkPropagasyonu(g, op, araBrep); if (!yuzRemap) return usdBirlestir(araBrep, araShape); std::vector araRemap; yuzGeriHaritaKur(s, araShape, op, araRemap); return usdBirlestir(araBrep, araShape, &araRemap, yuzRemap); }; if (mesafe > 0) { BRepAlgoAPI_Fuse op(s, halka); op.SetFuzzyValue(fuzzy); op.Build(); if (!op.IsDone()) { gunluk::uyari("Silindir it/çek: fuse başarısız"); return {}; } return tamamla(op.Shape(), op); } BRepAlgoAPI_Cut op(s, halka); op.SetFuzzyValue(fuzzy); op.Build(); if (!op.IsDone()) { gunluk::uyari("Silindir it/çek: cut başarısız"); return {}; } return tamamla(op.Shape(), op); } // Küresel yüz push/pull: eş-merkezli iki küre farkı (kabuk) → fuse (büyüt) / cut (küçült). // silindirRadyalOfset analoğu. delik (REVERSED) yüz → içe. MVP: TAM/tama-yakın küresel yüz // (kısmi küre yaması tüm küreyi etkiler — silindirin eksen-boyu sınırının küresel karşılığı yok). static BrepGovde kureRadyalOfset(const BrepGovde& g, const TopoDS_Shape& s, const TopoDS_Face& yuz, f64 mesafe, std::vector* yuzRemap = nullptr) { BRepAdaptor_Surface sf(yuz); const gp_Sphere sph = sf.Sphere(); const gp_Pnt c = sph.Location(); const f64 R = sph.Radius(); const bool delik = (yuz.Orientation() == TopAbs_REVERSED); const f64 Rn = delik ? (R - mesafe) : (R + mesafe); if (Rn < 1e-2) { gunluk::uyari("Küre it/çek: yarıçap çok küçük"); return {}; } const f64 Rin = std::min(R, Rn), Rout = std::max(R, Rn); const TopoDS_Shape disS = BRepPrimAPI_MakeSphere(c, Rout).Shape(); const TopoDS_Shape icS = BRepPrimAPI_MakeSphere(c, Rin).Shape(); const TopoDS_Shape kabuk = BRepAlgoAPI_Cut(disS, icS).Shape(); // küresel kabuk (araç) const auto tamamla = [&](const TopoDS_Shape& araShape, auto& op) -> BrepGovde { BrepGovde araBrep = sar(araShape); if (!araBrep.doluMu()) { gunluk::uyari("Küre it/çek: geçersiz sonuç"); return {}; } ic::renkPropagasyonu(g, op, araBrep); if (!yuzRemap) return usdBirlestir(araBrep, araShape); std::vector araRemap; // provenance: boolean + usd history zinciri yuzGeriHaritaKur(s, araShape, op, araRemap); return usdBirlestir(araBrep, araShape, &araRemap, yuzRemap); }; if (mesafe > 0) { BRepAlgoAPI_Fuse op(s, kabuk); if (!op.IsDone()) { gunluk::uyari("Küre it/çek: fuse başarısız"); return {}; } return tamamla(op.Shape(), op); } BRepAlgoAPI_Cut op(s, kabuk); if (!op.IsDone()) { gunluk::uyari("Küre it/çek: cut başarısız"); return {}; } return tamamla(op.Shape(), op); } // Konik yüz push/pull: yüzün eksen-boyu uç yarıçapları (R1,R2) radyal offset → frustum kabuk // (dış-iç MakeCone farkı) → fuse(büyüt)/cut(küçült). silindir/kure analoğu. delik→içe. Radyal // offset (gerçek normal semiAngle eğik) — taper hafif değişir; push/pull semantiği için kabul. static BrepGovde koniRadyalOfset(const BrepGovde& g, const TopoDS_Shape& s, const TopoDS_Face& yuz, f64 mesafe, std::vector* yuzRemap = nullptr) { BRepAdaptor_Surface sf(yuz); const gp_Cone kon = sf.Cone(); const gp_Pnt loc = kon.Axis().Location(); const gp_Dir dir = kon.Axis().Direction(); const gp_Vec dv(dir); const f64 refR = kon.RefRadius(); const f64 tanA = std::tan(kon.SemiAngle()); const bool delik = (yuz.Orientation() == TopAbs_REVERSED); f64 tmin = 1e300, tmax = -1e300; for (TopExp_Explorer v(yuz, TopAbs_VERTEX); v.More(); v.Next()) { const gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(v.Current())); const f64 t = gp_Vec(loc, p).Dot(dv); if (t < tmin) tmin = t; if (t > tmax) tmax = t; } if (tmax - tmin < 1e-4) { gunluk::uyari("Koni it/çek: eksen boyu belirsiz"); return {}; } const f64 boy = tmax - tmin; const f64 R1 = refR + tmin * tanA, R2 = refR + tmax * tanA; // uç yarıçaplar const f64 Rn1 = delik ? (R1 - mesafe) : (R1 + mesafe); const f64 Rn2 = delik ? (R2 - mesafe) : (R2 + mesafe); const f64 R1in = std::max(std::min(R1, Rn1), 0.0), R1out = std::max({R1, Rn1, 0.0}); const f64 R2in = std::max(std::min(R2, Rn2), 0.0), R2out = std::max({R2, Rn2, 0.0}); if (R1out < 1e-4 && R2out < 1e-4) { gunluk::uyari("Koni it/çek: yarıçap çok küçük"); return {}; } const gp_Ax2 ax2(loc.Translated(dv * tmin), dir); const TopoDS_Shape disS = BRepPrimAPI_MakeCone(ax2, R1out, R2out, boy).Shape(); const TopoDS_Shape icS = BRepPrimAPI_MakeCone(ax2, R1in, R2in, boy).Shape(); const TopoDS_Shape kabuk = BRepAlgoAPI_Cut(disS, icS).Shape(); // frustum kabuk (araç) const auto tamamla = [&](const TopoDS_Shape& araShape, auto& op) -> BrepGovde { BrepGovde araBrep = sar(araShape); if (!araBrep.doluMu()) { gunluk::uyari("Koni it/çek: geçersiz sonuç"); return {}; } ic::renkPropagasyonu(g, op, araBrep); if (!yuzRemap) return usdBirlestir(araBrep, araShape); std::vector araRemap; // provenance: boolean + usd history zinciri yuzGeriHaritaKur(s, araShape, op, araRemap); return usdBirlestir(araBrep, araShape, &araRemap, yuzRemap); }; if (mesafe > 0) { BRepAlgoAPI_Fuse op(s, kabuk); if (!op.IsDone()) { gunluk::uyari("Koni it/çek: fuse başarısız"); return {}; } return tamamla(op.Shape(), op); } BRepAlgoAPI_Cut op(s, kabuk); if (!op.IsDone()) { gunluk::uyari("Koni it/çek: cut başarısız"); return {}; } return tamamla(op.Shape(), op); } // ---- Yüz Taşı (Move Face — düz yüzü KEYFİ yönde ötele, komşu duvarları esnet) --------- // Push/pull'un genellemesi: normal yerine keyfi vektör (eğik hareket → duvarlar eğilir). // BRepFeat_MakePrism(yön=vec, Fuse=dışa/Cut=içe). Yön yüze paralelse (normal bileşen ~0) // dejenere → geçersiz. MVP: düz yüz. Eğri yüz radyal push/pull (yuzItCek) ile. BrepGovde yuzTasi(const BrepGovde& g, u32 yuzOrdinal, Vec3d vec) { const f64 boy = std::sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z); if (!g.gecerliMi() || yuzOrdinal == 0 || boy < 1e-6) return {}; occtSinyalKur(); try { const TopoDS_Shape s = tekSolidCoz(sekil(g)); // compound sarili boolean sonucu (PartDesign) desteklenir const TopoDS_Face yuz = ordinalYuz(s, yuzOrdinal); if (yuz.IsNull()) { gunluk::uyari("Yüz taşı: yüz eşleşmedi"); return {}; } BRepAdaptor_Surface sf(yuz); if (sf.GetType() != GeomAbs_Plane) { gunluk::uyari("Yüz taşı: yalnız düz yüz (MVP)"); return {}; } gp_Dir n = sf.Plane().Axis().Direction(); if (yuz.Orientation() == TopAbs_REVERSED) n.Reverse(); // dış normal const f64 normalBilesen = vec.x * n.X() + vec.y * n.Y() + vec.z * n.Z(); if (std::abs(normalBilesen) < 1e-6) { gunluk::uyari("Yüz taşı: yön yüze paralel (dejenere)"); return {}; } const bool fuse = normalBilesen > 0; // dışa hareket → malzeme ekle; içe → çıkar const gp_Dir yon(vec.x, vec.y, vec.z); BRepFeat_MakePrism mk(s, yuz, yuz, yon, fuse ? 1 : 0, Standard_True); mk.Perform(boy); if (!mk.IsDone()) { gunluk::uyari("Yüz taşı: BRepFeat başarısız"); return {}; } BrepGovde sonuc = sar(mk.Shape()); if (!sonuc.doluMu()) { gunluk::uyari("Yüz taşı: geçersiz sonuç"); return {}; } return sonuc; } catch (const Standard_Failure& h) { gunluk::uyari("Yüz taşı istisna: {}", h.what()); return {}; } } // Çoklu-yüz eş-zamanlı taşı (Plasticity multi-face move): TEK vektör N düz yüze SIRAYLA // uygulanır (yuzItCekCoklu'nun taşıma ikizi — aynı sözleşme: tek eleman = yuzTasi birebir, // boş liste = geçersiz). Her adım ordinalleri kaydırdığı için KALAN hedefler IsSame (yetmezse // geometrik) ile İLERİ taşınır; taşınamayan/başarısız yüz ATLANIR (kısmi sonuç > hiç sonuç, // yuzItCekCoklu ile aynı ilke). Yönün yüz normaline göre işareti adım adım çözülür: dışa = // malzeme ekle, içe = çıkar (yuzTasi kararı) → karşıt yüzlere aynı vektör = rijit öteleme. BrepGovde yuzTasiCoklu(const BrepGovde& g, const std::vector& yuzler, Vec3d vec) { if (yuzler.empty()) return {}; if (yuzler.size() == 1) return yuzTasi(g, yuzler[0], vec); const BrepGovde* su = &g; // BrepGovde move-only: güncel gövde işaretle, sonuç ara'da yaşar BrepGovde ara; std::vector kalan(yuzler.begin(), yuzler.end()); u32 uygulanan = 0; for (std::size_t i = 0; i < kalan.size(); ++i) { if (!kalan[i]) continue; // önceki adımla yutulmuş → atla BrepGovde sonuc = yuzTasi(*su, kalan[i], vec); if (!sonuc.doluMu()) { gunluk::uyari("Çoklu yüz taşı: yüz adımı başarısız (girdi ord {}) — atlandı", yuzler[i]); continue; } for (std::size_t k = i + 1; k < kalan.size(); ++k) { // kalan hedefleri İLERİ taşı if (!kalan[k]) continue; const u32 y = su->yuzIleriEsle(sonuc, kalan[k]); if (!y) gunluk::uyari("Çoklu yüz taşı: yüz {} önceki adımla değişti — atlanacak", yuzler[k]); kalan[k] = y; } ara = std::move(sonuc); su = &ara; ++uygulanan; } if (!uygulanan) return {}; return ara; } // ---- Çıkıntı/Blok Kaydır (Move Region) — kaynamış katıda bir çıkıntıyı taban düzlemi // boyunca RİJİT kaydırır (Plasticity "move region"). tabanNokta/tabanNormal = çıkıntının // oturduğu düzlem (kullanıcı taban yüzünü seçer); vec = öteleme (taban düzleminde kalması için // normale ~dik verilir). Yöntem: düzlemle Splitter → üst parça(lar)=çıkıntı(lar), alt=plaka; // seed'e en yakın üst parçayı vec ile ötele, hepsini Fuse. Footprint kendiliğinden iyileşir // (plaka üst yüzü düz kalır) — convexity/feature analizi GEREKMEZ. Ayrı gövde/parametrik yollar // çağıran tarafta ayrışır; bu yalnız TEK kaynamış katı içindir. CikintiTabanSonuc cikintiTabanBul(const BrepGovde& g, u32 seedYuz, bool tamGovdeHesapla) { CikintiTabanSonuc r; YuzBilgi yb; if (!g.gecerliMi() || seedYuz == 0 || !g.yuzBilgi(seedYuz, yb, /*alan*/ false) || !yb.duzlemMi) return r; const Vec3d n = yb.normal.birim(); // Bölge büyütme: seed'den ~DİK (duvar) komşulara yayıl; yatay yüzde dur (taban/tavan sınırı). std::vector bolge{seedYuz}, yigin{seedYuz}; while (!yigin.empty()) { const u32 f = yigin.back(); yigin.pop_back(); for (u32 kf : g.yuzKomsuYuzler(f)) { if (std::find(bolge.begin(), bolge.end(), kf) != bolge.end()) continue; YuzBilgi kb; if (!g.yuzBilgi(kf, kb, false)) continue; if (std::abs(kb.normal.birim().nokta(n)) < 0.35) { // ~dik → çıkıntı duvarı bolge.push_back(kf); yigin.push_back(kf); } } } // Taban kotu = bölge kontur noktalarının n boyunca EN KÜÇÜK izdüşümü (çıkıntının dibi). f64 h = 1e300; for (u32 f : bolge) for (const auto& lp : g.yuzKonturlari(f)) for (const Vec3d& p : lp) h = std::min(h, p.nokta(n)); if (h > 1e299) return r; r.nokta = yb.merkez - n * (yb.merkez.nokta(n) - h); r.normal = n; r.bolgeYuzler = std::move(bolge); if (tamGovdeHesapla) { // pahalı: tüm yüz sayımı (BRepAdaptor) — yalnız gerektiğinde const YuzTipOzet ozet = yuzTipleri(g); const int toplam = ozet.duzlemsel + ozet.silindirik + ozet.konik + ozet.kuresel + ozet.torus + ozet.digeri; r.tamGovde = (int)r.bolgeYuzler.size() >= toplam - 1; // yalnız taban ayırıyor → standalone katı } r.gecerli = r.bolgeYuzler.size() >= 2; // en az üst + 1 duvar return r; } // Ortak çekirdek: taban düzleminin ÜSTÜNDE kalan (seed'e en yakın) çıkıntıyı Splitter ile ayırır, // verilen gp_Trsf'i ona RİJİT uygular, kalanla Fuse eder → footprint kendiliğinden iyileşir. // cikintiKaydir (öteleme) ve yuzDondur (döndürme) bunu paylaşır (etiket loglar için). static BrepGovde cikintiTrsfUygula(const BrepGovde& g, Vec3d tabanNokta, Vec3d tabanNormal, Vec3d seedNokta, const gp_Trsf& tr, const char* etiket) { const f64 nlen = std::sqrt(tabanNormal.x * tabanNormal.x + tabanNormal.y * tabanNormal.y + tabanNormal.z * tabanNormal.z); if (!g.gecerliMi() || nlen < 1e-9) return {}; tabanNormal = tabanNormal * (1.0 / nlen); occtSinyalKur(); try { const TopoDS_Shape s = tekSolidCoz(sekil(g)); Bnd_Box bb; BRepBndLib::Add(s, bb); if (bb.IsVoid()) return {}; f64 x0, y0, z0, x1, y1, z1; bb.Get(x0, y0, z0, x1, y1, z1); const f64 diag = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0) + (z1 - z0) * (z1 - z0)) + 1.0; const gp_Pln pln(gp_Pnt(tabanNokta.x, tabanNokta.y, tabanNokta.z), gp_Dir(tabanNormal.x, tabanNormal.y, tabanNormal.z)); const TopoDS_Face duzlemYuz = BRepBuilderAPI_MakeFace(pln, -diag, diag, -diag, diag).Face(); BRepAlgoAPI_Splitter sp; TopTools_ListOfShape args, arac; args.Append(s); arac.Append(duzlemYuz); sp.SetArguments(args); sp.SetTools(arac); sp.Build(); if (!sp.IsDone()) { gunluk::uyari("{}: Splitter başarısız", etiket); return {}; } // Üst (çıkıntı) / alt (plaka) ayır; üstte seed'e EN YAKIN merkezli parça = dönüştürülecek. std::vector alt, ust; int hedefIdx = -1; f64 enYakin = 1e300; for (TopExp_Explorer ex(sp.Shape(), TopAbs_SOLID); ex.More(); ex.Next()) { const TopoDS_Shape sol = ex.Current(); GProp_GProps gp; BRepGProp::VolumeProperties(sol, gp); if (gp.Mass() < 1e-9) continue; // hacimsiz kırpıntı const gp_Pnt c = gp.CentreOfMass(); const f64 taraf = (c.X() - tabanNokta.x) * tabanNormal.x + (c.Y() - tabanNokta.y) * tabanNormal.y + (c.Z() - tabanNokta.z) * tabanNormal.z; if (taraf > 1e-6) { // düzlemin üstünde → çıkıntı const f64 d2 = (c.X() - seedNokta.x) * (c.X() - seedNokta.x) + (c.Y() - seedNokta.y) * (c.Y() - seedNokta.y) + (c.Z() - seedNokta.z) * (c.Z() - seedNokta.z); if (d2 < enYakin) { enYakin = d2; hedefIdx = (int)ust.size(); } ust.push_back(sol); } else { alt.push_back(sol); } } if (hedefIdx < 0 || alt.empty()) { gunluk::uyari("{}: taban/çıkıntı ayrışmadı (taban düzlemi doğru mu?)", etiket); return {}; } const TopoDS_Shape hedefDonusmus = BRepBuilderAPI_Transform(ust[(size_t)hedefIdx], tr, /*kopya*/ true).Shape(); // Tümünü birleştir: alt parçalar + üst parçalar (hedef dönüştürülmüş hâliyle). TopoDS_Shape acc; bool ilk = true; const auto ekle = [&](const TopoDS_Shape& parca) { if (ilk) { acc = parca; ilk = false; return; } BRepAlgoAPI_Fuse f(acc, parca); f.Build(); if (f.IsDone()) acc = f.Shape(); }; for (const auto& a : alt) ekle(a); for (int i = 0; i < (int)ust.size(); ++i) ekle(i == hedefIdx ? hedefDonusmus : ust[(size_t)i]); BrepGovde sonuc = sar(acc); if (!sonuc.doluMu()) { gunluk::uyari("{}: geçersiz sonuç", etiket); return {}; } return sonuc; } catch (const Standard_Failure& h) { gunluk::uyari("{} istisna: {}", etiket, h.what()); return {}; } } BrepGovde cikintiKaydir(const BrepGovde& g, Vec3d tabanNokta, Vec3d tabanNormal, Vec3d vec, Vec3d seedNokta) { gp_Trsf tr; tr.SetTranslation(gp_Vec(vec.x, vec.y, vec.z)); return cikintiTrsfUygula(g, tabanNokta, tabanNormal, seedNokta, tr, "Çıkıntı kaydır"); } BrepGovde yuzDondur(const BrepGovde& g, Vec3d tabanNokta, Vec3d tabanNormal, Vec3d eksenNokta, Vec3d eksenYon, f64 aciDerece, Vec3d seedNokta) { const f64 elen = std::sqrt(eksenYon.x * eksenYon.x + eksenYon.y * eksenYon.y + eksenYon.z * eksenYon.z); if (elen < 1e-9 || std::abs(aciDerece) < 1e-6) return {}; // eksensiz/sıfır açı → no-op eksenYon = eksenYon * (1.0 / elen); gp_Trsf tr; tr.SetRotation(gp_Ax1(gp_Pnt(eksenNokta.x, eksenNokta.y, eksenNokta.z), gp_Dir(eksenYon.x, eksenYon.y, eksenYon.z)), aciDerece * 0.01745329251994329577); // derece → radyan return cikintiTrsfUygula(g, tabanNokta, tabanNormal, seedNokta, tr, "Yüz döndür"); } // UnifySameDomain 1:1 yeniden-kurduğu yüzlere ESKİ üçgenlemeyi taşır. usd kozmetik // birleştirmede DEĞİŞMEYEN yüzlerin TShape'ini de yenileyebiliyor → incremental mesh // toptan ölüyordu (devasa modelde usd'yi eşikle atlatmanın nedeni buydu; atlayınca da // uzatılan yan duvar komşusuyla BİRLEŞMEYİP yama gibi görünüyordu). Yalnız güvenli durum: // tek-eşleme (merge DEĞİL) + aynı yüzey handle'ı + aynı location. Merge edilen/yeni yüzler // üçgenlemesiz kalır → remesh yalnız onlara. static void usdUcgenlemeTasi(const TopoDS_Shape& ara, const TopoDS_Shape& son, const Handle(BRepTools_History)& hist) { if (hist.IsNull()) return; TopTools_IndexedMapOfShape sonIdx; for (TopExp_Explorer k(son, TopAbs_FACE); k.More(); k.Next()) sonIdx.Add(k.Current()); std::vector sayi((size_t)sonIdx.Extent() + 1, 0); std::vector kaynak((size_t)sonIdx.Extent() + 1); for (TopExp_Explorer a(ara, TopAbs_FACE); a.More(); a.Next()) { if (hist->IsRemoved(a.Current())) continue; const TopTools_ListOfShape& mod = hist->Modified(a.Current()); if (mod.IsEmpty()) continue; // aynı TShape sonda da yaşıyor → üçgenleme zaten korunur for (const TopoDS_Shape& sf : mod) { const int idx = sonIdx.FindIndex(sf); if (idx >= 1) { ++sayi[(size_t)idx]; kaynak[(size_t)idx] = a.Current(); } } } BRep_Builder b; for (int i = 1; i <= sonIdx.Extent(); ++i) { if (sayi[(size_t)i] != 1) continue; // merge sonucu / eşleme yok → remesh doğru davranış const TopoDS_Face sf = TopoDS::Face(sonIdx(i)); const TopoDS_Face af = TopoDS::Face(kaynak[(size_t)i]); if (!af.Location().IsEqual(sf.Location())) continue; TopLoc_Location la; const Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(af, la); if (tri.IsNull()) continue; TopLoc_Location l1, l2; if (BRep_Tool::Surface(af, l1) != BRep_Tool::Surface(sf, l2) || !l1.IsEqual(l2)) continue; // yüzey birebir paylaşılmıyorsa (gerçek değişim) taşıma b.UpdateFace(sf, tri); } } // ---- Yüz İt/Çek (Push/Pull — BRepFeat_MakePrism local feature) --------- // Gerçek yüzü (delikleriyle) profil alıp normali boyunca prism; boolean YOK → topoloji // korunur (delikler doğru taşınır), fuse/cut'tan hızlı. mesafe>0 ekle, <0 çıkar. // Silindirik yüzde radyal offset (annulus boolean) dalına düşer. BrepGovde yuzItCek(const BrepGovde& g, u32 yuzOrdinal, f64 mesafe, std::vector* yuzRemap) { if (yuzRemap) yuzRemap->clear(); if (!g.gecerliMi() || yuzOrdinal == 0 || std::abs(mesafe) < 1e-6) return {}; try { const TopoDS_Shape s = tekSolidCoz(sekil(g)); // compound sarili boolean sonucu (PartDesign) desteklenir const TopoDS_Face yuz = ordinalYuz(s, yuzOrdinal); if (yuz.IsNull()) { gunluk::uyari("Yüz it/çek: yüz eşleşmedi"); return {}; } BRepAdaptor_Surface sf(yuz); gp_Pnt sloc; gp_Dir sdir; f64 sR; if (silindirParam(sf, sloc, sdir, sR)) // canonical silindir VEYA extrude-daire return silindirRadyalOfset(g, s, yuz, sloc, sdir, sR, mesafe, yuzRemap); if (sf.GetType() == GeomAbs_Sphere) return kureRadyalOfset(g, s, yuz, mesafe, yuzRemap); if (sf.GetType() == GeomAbs_Cone) return koniRadyalOfset(g, s, yuz, mesafe, yuzRemap); if (sf.GetType() != GeomAbs_Plane) { gunluk::uyari("Yüz it/çek: yalnız düz/silindir/küre/koni yüz"); return {}; } gp_Dir n = sf.Plane().Axis().Direction(); if (yuz.Orientation() == TopAbs_REVERSED) n.Reverse(); // dış normal const bool ekle = mesafe > 0; const gp_Dir yon = ekle ? n : gp_Dir(-n.X(), -n.Y(), -n.Z()); // Sbase=gövde, Pbase=Skface=uzatılacak yüz, Fuse(1=ekle,0=çıkar), Modify=true. BRepFeat_MakePrism mk(s, yuz, yuz, yon, ekle ? 1 : 0, Standard_True); mk.Perform(std::abs(mesafe)); if (!mk.IsDone()) { gunluk::uyari("Yüz it/çek: BRepFeat başarısız"); return {}; } const TopoDS_Shape araShape = mk.Shape(); // STEP per-yüz renkleri önce ARA şekle taşınır (mk history ile), sonra usd ile son'a. BrepGovde araBrep = sar(araShape); ic::renkPropagasyonu(g, mk, araBrep); // DEVASA GÖVDE EŞİĞİ (usdBirlestir ile aynı gerekçe): kozmetik koplanar birleştirme // atlanır — BRepFeat değişmeyen TShape'leri korur → üçgenleme/kenar önbellekleri geçerli // kalır (her it/çek tam remesh olmaz), usd'nin kendisi de saniyeler sürmez. { u32 yuzSayi = 0; for (TopExp_Explorer yk(araShape, TopAbs_FACE); yk.More(); yk.Next()) ++yuzSayi; if (yuzSayi > usdYuzEsigi()) { if (!araBrep.doluMu()) { gunluk::uyari("Yüz it/çek: geçersiz sonuç"); return {}; } if (yuzRemap) { yuzGeriHaritaKur(s, araShape, mk, *yuzRemap); TopTools_IndexedMapOfShape sonIdx; for (TopExp_Explorer yk(araShape, TopAbs_FACE); yk.More(); yk.Next()) sonIdx.Add(yk.Current()); for (const TopoDS_Shape& sy : mk.LastShape()) { // ötelenen üst yüz → base ordinal const int idx = sonIdx.FindIndex(sy); if (idx >= 1 && (size_t)idx <= yuzRemap->size() && (*yuzRemap)[idx - 1] == 0) (*yuzRemap)[idx - 1] = (int)yuzOrdinal; } } return araBrep; } } // ⭐ UZATILAN YÜZEY BİRLEŞİK: koplanar komşu yüz/kenarları TEK yüze birleştir → uzatılan // yüzey ayrık durmaz, komşusuyla bütünleşir (kullanıcı isteği). ordinal kayar → renk/yuzOz // iki aşamalı history (mk → usd) ile taşınır. ShapeUpgrade_UnifySameDomain usd(araShape, Standard_True, Standard_True, Standard_False); usd.Build(); const TopoDS_Shape sonShape = usd.Shape(); BrepGovde sonuc = sar(sonShape); if (!sonuc.doluMu()) { gunluk::uyari("Yüz it/çek: geçersiz sonuç"); return {}; } Handle(BRepTools_History) usdHist = usd.History(); usdUcgenlemeTasi(araShape, sonShape, usdHist); // 1:1 yüzlerde incremental mesh yaşasın TopTools_IndexedMapOfShape sonIdx; // son şekil yüz ordinal haritası for (TopExp_Explorer k(sonShape, TopAbs_FACE); k.More(); k.Next()) sonIdx.Add(k.Current()); // Bir ARA yüzü → SON yüz(ler)e uygula (usd Modified; boşsa değişmedi = kendisi). const auto araSon = [&](const TopoDS_Shape& araYuz, const auto& boya) { if (usdHist->IsRemoved(araYuz)) return; const TopTools_ListOfShape& mod = usdHist->Modified(araYuz); if (mod.IsEmpty()) boya(araYuz); else for (const TopoDS_Shape& sf2 : mod) boya(sf2); }; // STEP renkleri: araBrep.yuzRenkleri → sonuc (usd history üzerinden). if (araBrep.yuzRenkliMi()) { const auto& ar = araBrep.yuzRenkleri(); std::vector sr(sonIdx.Extent(), Vec3f{0.62f, 0.66f, 0.72f}); u32 ao = 0; for (TopExp_Explorer ak(araShape, TopAbs_FACE); ak.More(); ak.Next()) { ++ao; const Vec3f c = (ao <= ar.size()) ? ar[ao - 1] : Vec3f{0.62f, 0.66f, 0.72f}; araSon(ak.Current(), [&](const TopoDS_Shape& sf2) { const int idx = sonIdx.FindIndex(sf2); if (idx >= 1 && (size_t)idx <= sr.size()) sr[idx - 1] = c; }); } sonuc.yuzRenkleriAta(std::move(sr)); } // Remap (yuzOz/seçim): eski yüz --mk--> ara yüz --usd--> son yüz. LastShape (ötelenen üst) // base ordinaline (yuzOrdinal) bağlanır → seçili renkli yüzün rengi kaybolmaz. if (yuzRemap) { yuzRemap->assign(sonIdx.Extent(), 0); const auto sonAta = [&](const TopoDS_Shape& araYuz, int ord) { araSon(araYuz, [&](const TopoDS_Shape& sf2) { const int idx = sonIdx.FindIndex(sf2); if (idx >= 1 && (size_t)idx <= yuzRemap->size() && (*yuzRemap)[idx - 1] == 0) (*yuzRemap)[idx - 1] = ord; }); }; u32 o = 0; for (TopExp_Explorer ek(s, TopAbs_FACE); ek.More(); ek.Next()) { ++o; const TopoDS_Shape& ef = ek.Current(); if (!mk.IsDeleted(ef)) sonAta(ef, (int)o); for (const TopoDS_Shape& yf : mk.Modified(ef)) sonAta(yf, (int)o); for (const TopoDS_Shape& yf : mk.Generated(ef)) sonAta(yf, (int)o); } for (const TopoDS_Shape& sy : mk.LastShape()) sonAta(sy, (int)yuzOrdinal); } return sonuc; } catch (const Standard_Failure& h) { gunluk::hata("Yüz it/çek hatası: {}", h.what()); return {}; } } BrepGovde yuzItCekCoklu(const BrepGovde& g, const std::vector& yuzler, f64 mesafe, std::vector* yuzRemap) { if (yuzRemap) yuzRemap->clear(); if (yuzler.empty()) return {}; if (yuzler.size() == 1) return yuzItCek(g, yuzler[0], mesafe, yuzRemap); const BrepGovde* su = &g; // BrepGovde move-only: güncel gövde işaretle, sonuç ara'da yaşar BrepGovde ara; std::vector kalan(yuzler.begin(), yuzler.end()); std::vector toplam; // güncel ordinal → İLK girdi ordinali u32 uygulanan = 0; for (std::size_t i = 0; i < kalan.size(); ++i) { if (!kalan[i]) continue; // önceki adımla yutulmuş → atla std::vector adim; BrepGovde sonuc = yuzItCek(*su, kalan[i], mesafe, &adim); if (!sonuc.doluMu()) { gunluk::uyari("Çoklu it/çek: yüz adımı başarısız (girdi ord {}) — atlandı", yuzler[i]); continue; } if (toplam.empty()) { toplam = adim; } else { // kompozisyon: yeni→önceki→...→ilk girdi std::vector yeni(adim.size(), 0); for (std::size_t j = 0; j < adim.size(); ++j) if (adim[j] > 0 && (std::size_t)adim[j] <= toplam.size()) yeni[j] = toplam[adim[j] - 1]; toplam = std::move(yeni); } for (std::size_t k = i + 1; k < kalan.size(); ++k) { // kalan hedefleri İLERİ taşı if (!kalan[k]) continue; u32 y = su->yuzIleriEsle(sonuc, kalan[k]); if (!y) for (std::size_t j = 0; j < adim.size(); ++j) if (adim[j] > 0 && (u32)adim[j] == kalan[k]) { y = (u32)(j + 1); break; } if (!y) gunluk::uyari("Çoklu it/çek: yüz {} önceki adımla değişti — atlanacak", yuzler[k]); kalan[k] = y; } ara = std::move(sonuc); su = &ara; ++uygulanan; } if (!uygulanan) return {}; if (yuzRemap) *yuzRemap = std::move(toplam); return ara; } // ---- Blend-koruyan Yüz İt/Çek ("Grow: Moving" tam hali — SpaceClaim kalıbı) ---------- // SORUN: yuzItCek her yüzü KENDİ normali boyunca öteler. Boss üstünü kaldırınca çevresindeki // fillet ÖTELENMEZ, yarıçapı değişir → blend bozulur. Bu yüzden çağıran taraf teğet yayılımına // fillet'i katamıyordu. // ÇÖZÜM (ticari CAD'lerin yaptığı): blend'i SÖK → keskin köşede öteleme yap → AYNI yarıçapla // yeniden KUR. Üç adımın da ordinalleri kayar; her adımda yüzler ileri taşınır. // Blend bulunmazsa/adım başarısız olursa DAVRANIŞ DEĞİŞMEZ: düz yuzItCekCoklu'ya düşülür. // MVP kapsam: YUVARLAK blend (silindir/torus). Düz pah (chamfer) yeniden kurulmaz — düzlem // olduğu için sökülmez, komşu olarak esner (eski davranış). BrepGovde yuzItCekBlendKorur(const BrepGovde& g, const std::vector& yuzler, f64 mesafe, f64 aciTolDeg, std::vector* yuzRemap) { if (yuzRemap) yuzRemap->clear(); if (!g.gecerliMi() || yuzler.empty() || std::abs(mesafe) < 1e-6) return {}; const auto hareketliMi = [&yuzler](u32 o) { return std::find(yuzler.begin(), yuzler.end(), o) != yuzler.end(); }; // 1) Hareket eden yüzlere DOĞRUDAN teğet (kenar paylaşan) yuvarlak blend yüzleri. // tanjantYuzler GEÇİŞLİ yürür (fillet üzerinden duvara da atlar) → yalnız tanjant kümesi // yetmez, komşuluk ŞARTI da gerekir; yoksa boss'un silindirik DUVARI "blend" sanılırdı. struct BlendKayit { u32 blend = 0; f64 r = 0; std::vector> ciftler; // (hareketli yüz, karşı duvar) — fillet kenarı burada }; std::vector blendler; for (u32 m : yuzler) { const std::vector komsu = g.yuzKomsuYuzler(m); for (u32 o : g.tanjantYuzler(m, aciTolDeg)) { if (o == m || hareketliMi(o)) continue; if (std::find(komsu.begin(), komsu.end(), o) == komsu.end()) continue; // dolaylı → blend değil if (std::any_of(blendler.begin(), blendler.end(), [o](const BlendKayit& b) { return b.blend == o; })) continue; YuzBilgi yb; if (!g.yuzBilgi(o, yb)) continue; const f64 r = yb.torusMu ? yb.kucukYaricap : (yb.silindirMi ? yb.yaricap : 0.0); if (r <= 1e-6) continue; // düzlem (pah) / koni / küre → yuvarlak blend değil BlendKayit bk; bk.blend = o; bk.r = r; // Blend'in komşuları arasında yalnız TEĞET olanlar "karşı duvar"dır. Bir fillet yüzü // uzun kenarlarında teğet (üst yüz + duvar) ama UÇLARINDA dik yüzlerle KESKİN birleşir; // uçları da duvar sayarsak öteleme sonrası oralara da fillet atılır (yaşandı: 1 yerine // 3 kenar yuvarlandı, hacim tutmadı, yüz sayısı 7 yerine 9 çıktı). const std::vector blendTanjant = g.tanjantYuzler(o, aciTolDeg); for (u32 d : g.yuzKomsuYuzler(o)) { if (hareketliMi(d) || d == o) continue; if (std::find(blendTanjant.begin(), blendTanjant.end(), d) == blendTanjant.end()) continue; bk.ciftler.push_back({m, d}); } if (bk.ciftler.empty()) continue; // yalnız hareketli yüzlere komşu → yeniden kurulacak kenar yok blendler.push_back(std::move(bk)); } } if (blendler.empty()) return yuzItCekCoklu(g, yuzler, mesafe, yuzRemap); // Ordinal ileri taşıma: önce KESİN IsSame eşlemesi, olmazsa adımın remap'ini ters çevir. const auto ileri = [](const BrepGovde& eski, const BrepGovde& yeni, const std::vector& remap, u32 ord) -> u32 { if (!ord) return 0; if (const u32 y = eski.yuzIleriEsle(yeni, ord)) return y; for (std::size_t j = 0; j < remap.size(); ++j) if (remap[j] > 0 && (u32)remap[j] == ord) return (u32)(j + 1); return 0; }; // 2) Blend'leri SÖK (Defeaturing: komşular uzatılıp keskin köşe oluşur). std::vector silinecek; silinecek.reserve(blendler.size()); for (const BlendKayit& b : blendler) silinecek.push_back(b.blend); std::vector remapSok; BrepGovde sade = yuzKaldir(g, silinecek, &remapSok); if (!sade.doluMu()) { gunluk::uyari("Blend-korur it/çek: blend sökülemedi ({} yüz) — düz it/çek'e düşülüyor", silinecek.size()); return yuzItCekCoklu(g, yuzler, mesafe, yuzRemap); } struct Cift { u32 tasinan = 0, duvar = 0; f64 r = 0; bool islendi = false; }; std::vector ciftler; for (const BlendKayit& b : blendler) for (const auto& [m, d] : b.ciftler) ciftler.push_back({ileri(g, sade, remapSok, m), ileri(g, sade, remapSok, d), b.r, false}); std::vector hareketliSade; for (u32 m : yuzler) if (const u32 n = ileri(g, sade, remapSok, m)) hareketliSade.push_back(n); if (hareketliSade.empty()) { gunluk::uyari("Blend-korur it/çek: hareketli yüz sökme sonrası izlenemedi — düz it/çek"); return yuzItCekCoklu(g, yuzler, mesafe, yuzRemap); } // 3) Keskin gövdede ötele. std::vector remapTasi; BrepGovde tasinmis = yuzItCekCoklu(sade, hareketliSade, mesafe, &remapTasi); if (!tasinmis.doluMu()) { gunluk::uyari("Blend-korur it/çek: sökülmüş gövdede öteleme başarısız — düz it/çek"); return yuzItCekCoklu(g, yuzler, mesafe, yuzRemap); } for (Cift& c : ciftler) { c.tasinan = ileri(sade, tasinmis, remapTasi, c.tasinan); c.duvar = ileri(sade, tasinmis, remapTasi, c.duvar); } // 4) Blend'i AYNI yarıçapla yeniden kur. Yarıçap grubu başına tek MakeFillet; her fillet // ordinalleri kaydırdığı için kalan çiftler her turda ileri taşınır. const auto ortakKenarlar = [](const BrepGovde& b, u32 y1, u32 y2) { std::vector ks; if (!y1 || !y2) return ks; u32 n = 0; for (TopExp_Explorer e(tekSolidCoz(sekil(b)), TopAbs_EDGE); e.More(); e.Next()) { ++n; const std::vector ky = b.kenarKomsuYuzler(n); if (ky.size() == 2 && ((ky[0] == y1 && ky[1] == y2) || (ky[0] == y2 && ky[1] == y1))) ks.push_back(n); } return ks; }; BrepGovde son = std::move(tasinmis); u32 kurulan = 0; for (;;) { f64 r = 0; for (const Cift& c : ciftler) if (!c.islendi) { r = c.r; break; } if (r <= 0) break; // tüm çiftler işlendi std::vector kenarlar; for (Cift& c : ciftler) { if (c.islendi || std::abs(c.r - r) > 1e-9) continue; c.islendi = true; for (u32 k : ortakKenarlar(son, c.tasinan, c.duvar)) kenarlar.push_back(k); } if (kenarlar.empty()) { gunluk::uyari("Blend-korur it/çek: R={} için yeniden kurulacak kenar bulunamadı", r); continue; } std::sort(kenarlar.begin(), kenarlar.end()); kenarlar.erase(std::unique(kenarlar.begin(), kenarlar.end()), kenarlar.end()); std::vector remapFil; BrepGovde yuvarlanmis = yuvarlat(son, kenarlar, r, &remapFil); if (!yuvarlanmis.doluMu()) { // Fillet kurulamadı: KESKİN köşeli sonucu koru (öteleme geçerli), kullanıcı görsün. gunluk::uyari("Blend-korur it/çek: R={} blend yeniden kurulamadı — köşe keskin kaldı", r); continue; } for (Cift& c : ciftler) { if (c.islendi) continue; // işlenmişin ordinali artık gerekmez c.tasinan = ileri(son, yuvarlanmis, remapFil, c.tasinan); c.duvar = ileri(son, yuvarlanmis, remapFil, c.duvar); } son = std::move(yuvarlanmis); ++kurulan; } if (!kurulan) { gunluk::uyari("Blend-korur it/çek: hiçbir blend yeniden kurulamadı — düz it/çek'e düşülüyor"); return yuzItCekCoklu(g, yuzler, mesafe, yuzRemap); } // Remap: zincir boyunca kompozisyon kırılgan (sök+ötele+fillet) → geometrik eşleme ile // İLK girdiye göre tek seferde kurulur (renk/provenance taşıma bunu kullanır). if (yuzRemap) *yuzRemap = yuzGeometrikEsle(g, son); gunluk::bilgi("[blend-korur] {} blend söküldü, {} yarıçap grubu yeniden kuruldu", blendler.size(), kurulan); return son; } std::vector yuzGeometrikEsle(const BrepGovde& eski, const BrepGovde& yeni) { std::vector harita; if (!yeni.gecerliMi()) return harita; // 1) KESİN ön-geçiş: işlemden DEĞİŞMEDEN geçen yüzler TShape+Location'ını korur (OCCT // boolean/fillet/offset dokunmadığını kopyalamaz) → IsSame ile O(N) eşleme, GProp yok. // Devasa modelde (binlerce yüz) O(N²) merkez/alan karşılaştırması yalnız KALAN birkaç // değişmiş yüze iner — feature işlem süresini model boyutundan bağımsızlaştırır. TopTools_IndexedMapOfShape eskiIdx; if (eski.gecerliMi()) for (TopExp_Explorer e(sekil(eski), TopAbs_FACE); e.More(); e.Next()) eskiIdx.Add(e.Current()); std::vector kalan; // geometrik eşlenecek yeni ordinaller { u32 j = 0; for (TopExp_Explorer k(sekil(yeni), TopAbs_FACE); k.More(); k.Next()) { ++j; const int idx = eskiIdx.Extent() ? eskiIdx.FindIndex(k.Current()) : 0; harita.push_back(idx >= 1 ? idx : 0); if (idx < 1) kalan.push_back(j); } } if (kalan.empty() || !eski.gecerliMi()) return harita; // 2) Kalanlar: geometrik imza — yalnız ön-geçişte KULLANILMAMIŞ eski yüzlere karşı // (kullanılan zaten birebir eşlendi; ikinci kez eşlenmesin). İKİ kademe: // (a) KESİN: merkez tam çakışık (+normal+alan) — değişmeden geçen ama TShape'i // yenilenmiş yüz. (b) YÜZEY KİMLİĞİ: fillet/it-çek komşu yüzleri KIRPAR (merkez // kayar, alan değişir) ama taşıyıcı YÜZEY aynı kalır — aynı düzlem/silindir/küre // üzerindeki en yakın-merkezli eski yüz eşlenir. Bu olmadan her işlem birkaç // komşunun rengini/yuzOz'unu düşürüyordu → birkaç işlemde "tüm gövde tek renk". std::vector kullanildi((size_t)eskiIdx.Extent() + 1, 0); for (int v : harita) if (v > 0 && (size_t)v < kullanildi.size()) kullanildi[(size_t)v] = 1; struct Imza { Vec3d m, n, eksen, eksenNokta; f64 a, yaricap; int tip; }; // tip: 0 düz · 1 silindir · 2 küre · 3 diğer std::vector es((size_t)eskiIdx.Extent()); std::vector esVar((size_t)eskiIdx.Extent(), 0); for (u32 i = 1; i <= (u32)eskiIdx.Extent(); ++i) { if (kullanildi[i]) continue; YuzBilgi yb; if (!eski.yuzBilgi(i, yb)) continue; const int tip = yb.duzlemMi ? 0 : (yb.silindirMi ? 1 : (yb.kureMi ? 2 : 3)); es[i - 1] = {yb.merkez, yb.normal, yb.eksen, yb.eksenNokta, yb.alan, yb.yaricap, tip}; esVar[i - 1] = 1; } // Aynı taşıyıcı yüzey mi? (tol mm mertebesi — modelleme toleransı; oryantasyon dahil: // ters bakan koplanar yüz eşlenmez). Koni/serbest yüzey: imza yok → yalnız kesin kademe. const auto ayniYuzey = [](const Imza& s, const YuzBilgi& y) -> bool { constexpr f64 tol = 1e-3; if (s.tip == 0 && y.duzlemMi) { if (s.n.nokta(y.normal) < 0.9999) return false; return std::abs(s.n.nokta(s.m) - y.normal.nokta(y.merkez)) < tol; // düzlem ofseti } if (s.tip == 1 && y.silindirMi) { const Vec3d e1 = s.eksen.birim(), e2 = y.eksen.birim(); if (std::abs(e1.nokta(e2)) < 0.9999) return false; if (std::abs(s.yaricap - y.yaricap) > tol) return false; const Vec3d f = y.eksenNokta - s.eksenNokta; const Vec3d dik = f - e1 * f.nokta(e1); // eksen doğruları arası mesafe return dik.uzunlukKare() < tol * tol; } if (s.tip == 2 && y.kureMi) return std::abs(s.yaricap - y.yaricap) < tol && (s.eksenNokta - y.eksenNokta).uzunlukKare() < tol * tol; return false; }; for (u32 j : kalan) { YuzBilgi yb; if (!yeni.yuzBilgi(j, yb)) continue; int enIyi = 0, enYuzey = 0; f64 enSkor = 1e300, enYuzeySkor = 1e300; for (size_t k = 0; k < es.size(); ++k) { if (!esVar[k]) continue; const Imza& s = es[k]; const f64 dm = (s.m - yb.merkez).uzunlukKare(); if (dm < enSkor && !(s.tip == 0 && yb.duzlemMi && s.n.nokta(yb.normal) < 0.98) && std::abs(s.a - yb.alan) <= 0.05 * std::max(s.a, 1e-9)) { // normal uyumu + alan %5 enSkor = dm; enIyi = (int)k + 1; } if (dm < enYuzeySkor && ayniYuzey(s, yb)) { enYuzeySkor = dm; enYuzey = (int)k + 1; } } // (a) Değişmeyen yüz merkezi TAM çakışık (dm≈0, eşik 0.05mm²) → kesin eşle. // (b) Değilse aynı taşıyıcı yüzeydeki en yakın eski yüz (kırpılmış komşu). GERÇEKTEN // yeni yüzeyler (fillet bevel'i, ötelenen düzlem) hiçbir eski yüzeyle çakışmadığından // 0 kalır → yeni özellik alır, yanlış renk taşınmaz. if (enSkor < 0.05 * 0.05) harita[j - 1] = enIyi; else if (enYuzey) harita[j - 1] = enYuzey; } return harita; } // ---- Lump (ayrık katı parça) — CATIA Remove Lump / Union Trim kırp ----- int lumpSayisi(const BrepGovde& g) { if (!g.gecerliMi()) return 0; int n = 0; for (TopExp_Explorer s(sekil(g), TopAbs_SOLID); s.More(); s.Next()) ++n; return n; } f64 lumpEnKucukBosluk(const BrepGovde& g) { if (!g.gecerliMi()) return -1.0; std::vector parcalar; for (TopExp_Explorer s(sekil(g), TopAbs_SOLID); s.More(); s.Next()) parcalar.push_back(TopoDS::Solid(s.Current())); if (parcalar.size() < 2) return -1.0; f64 enKucuk = 1e300; try { for (size_t i = 0; i < parcalar.size(); ++i) for (size_t j = i + 1; j < parcalar.size(); ++j) { BRepExtrema_DistShapeShape hesap(parcalar[i], parcalar[j]); if (hesap.IsDone() && hesap.Value() < enKucuk) enKucuk = hesap.Value(); } } catch (const Standard_Failure& h) { gunluk::hata("Lump boşluk hesabı hatası: {}", h.what()); return -1.0; } return enKucuk < 1e300 ? enKucuk : -1.0; } BrepGovde lumpAyikla(const BrepGovde& g, const std::vector& yuzler, bool tut) { if (!g.gecerliMi() || yuzler.empty()) return {}; try { const TopoDS_Shape s = sekil(g); // Seçili 1-tabanlı yüz ordinalleri → TopoDS_Face (TopExp FACE sırası; pick ile aynı). std::vector seciliYuzler = ordinalYuzler(s, yuzler); if (seciliYuzler.empty()) { gunluk::uyari("Lump: yüz eşleşmedi"); return {}; } // Ayrık katı parçalar. Her parçanın kendi yüz haritası → seçili yüz ait mi? std::vector tutulan; int parcaSayi = 0; for (TopExp_Explorer se(s, TopAbs_SOLID); se.More(); se.Next()) { ++parcaSayi; const TopoDS_Solid kati = TopoDS::Solid(se.Current()); TopTools_IndexedMapOfShape yuzHarita; TopExp::MapShapes(kati, TopAbs_FACE, yuzHarita); bool iceriyor = false; for (const TopoDS_Face& y : seciliYuzler) if (yuzHarita.Contains(y)) { iceriyor = true; break; } if (iceriyor == tut) tutulan.push_back(kati); // tut→seçili kalır; !tut→seçilmeyen kalır } if (parcaSayi <= 1) { gunluk::uyari("Lump: gövde tek parça (ayrık katı yok)"); return {}; } if (tutulan.empty()) { gunluk::uyari("Lump: sonuçta parça kalmadı"); return {}; } if (tutulan.size() == 1) return sar(tutulan.front()); // tek parça → doğrudan solid TopoDS_Compound bilesik; // çok parça → compound BRep_Builder kur; kur.MakeCompound(bilesik); for (const TopoDS_Solid& k : tutulan) kur.Add(bilesik, k); return sar(bilesik); } catch (const Standard_Failure& h) { gunluk::hata("Lump ayıklama hatası: {}", h.what()); return {}; } } // ---- Yüz Draft (kalıp çıkış açısı — seçili yüzlere) -------------------- namespace { // Yansima hatti (reflect/silhouette) noktasi: yuzey uzerinde normalin cekme yonune EN // DIK oldugu (|n·cekme| minimum) nokta. u,v grid tarama (MVP; tam silhouette egrisi degil). // Bulunamazsa false (yuz merkezine dusulur). bool reflectNoktaBul(const TopoDS_Face& y, const gp_Dir& cekme, gp_Pnt& cikti) { try { BRepAdaptor_Surface sf(y); f64 u0 = sf.FirstUParameter(), u1 = sf.LastUParameter(); f64 v0 = sf.FirstVParameter(), v1 = sf.LastVParameter(); if (Precision::IsInfinite(u0) || Precision::IsInfinite(u1) || Precision::IsInfinite(v0) || Precision::IsInfinite(v1)) return false; const int N = 12; f64 enIyi = 1e18; bool bulundu = false; for (int i = 0; i <= N; ++i) for (int j = 0; j <= N; ++j) { const f64 u = u0 + (u1 - u0) * (f64)i / (f64)N; const f64 v = v0 + (v1 - v0) * (f64)j / (f64)N; BRepLProp_SLProps p(sf, u, v, 1, 1e-7); if (!p.IsNormalDefined()) continue; const f64 d = std::abs(p.Normal().Dot(cekme)); if (d < enIyi) { enIyi = d; cikti = p.Value(); bulundu = true; } } return bulundu; } catch (const Standard_Failure&) { return false; } } } // namespace BrepGovde kenardanYuzEg(const BrepGovde& g, u32 kenarOrd, u32 yuzOrd, f64 aciDerece) { if (!g.gecerliMi() || kenarOrd == 0 || yuzOrd == 0 || std::abs(aciDerece) < 1e-6) return {}; KenarBilgi kb; if (!g.kenarBilgi(kenarOrd, kb) || !kb.dogruMu) { gunluk::uyari("Kenardan eğme: düz kenar gerekli"); return {}; } YuzBilgi yb; if (!g.yuzBilgi(yuzOrd, yb, /*alanHesapla=*/false) || !yb.duzlemMi) { gunluk::uyari("Kenardan eğme: düz yüz gerekli (MVP)"); return {}; } const Vec3d n = yb.normal.birim(); // DraftAngle sözleşmesi: eğilen yüz çekme yönüne PARALEL duvar olmalı. Menteşe=kenar // için çekme = YÜZ DÜZLEMİNDE, kenara DİK yön (n × kenarYön), menteşeden yüz merkezine // doğru yönlendirilir; nötr düzlem = kenardan geçen, normali çekme → yüz ∩ nötr = kenar. Vec3d cek = n.capraz(kb.yon); if (cek.uzunlukKare() < 1e-12) { gunluk::uyari("Kenardan eğme: kenar/yüz dejenere"); return {}; } cek = cek.birim(); const Vec3d ic{yb.merkez.x - kb.orta.x, yb.merkez.y - kb.orta.y, yb.merkez.z - kb.orta.z}; if (cek.nokta(ic) < 0.0) cek = {-cek.x, -cek.y, -cek.z}; // işaret kararlı: içeri doğru return yuzDraft(g, {yuzOrd}, aciDerece, cek, kb.orta, cek, false); } // ---- Kenar Taşı (Fikri 07-22 "kenardan uzat: SADECE o kenar hareket etsin"): çekilen kenar, // seçili yüzün düzleminde kenara-dik DIŞARI yönde mesafe kadar ötelenir. Seçili yüz kendi // düzleminde uzar (karşı kenarı sabit); ÖTEKİ komşu yüz, kendi uzak paralel kenarı (menteşe) // etrafında atan(mesafe/L) ile eğilir (DraftAngle) — iki yüzün de diğer kenarları yerinde kalır. // DraftAngle açı işareti uzlaşımı kırılgan → sonuçta kenarın beklenen konuma gittiği doğrulanır, // gitmediyse ters açı denenir (tahmin yok, deterministik seçim). BrepGovde kenarTasi(const BrepGovde& g, u32 kenarOrd, u32 yuzOrd, f64 mesafe) { if (!g.gecerliMi() || kenarOrd == 0 || yuzOrd == 0 || std::abs(mesafe) < 1e-9) return {}; KenarBilgi kb; if (!g.kenarBilgi(kenarOrd, kb) || !kb.dogruMu) { gunluk::uyari("Kenar taşı: düz kenar gerekli"); return {}; } YuzBilgi ya; if (!g.yuzBilgi(yuzOrd, ya, /*alanHesapla=*/false) || !ya.duzlemMi) { gunluk::uyari("Kenar taşı: düz yüz gerekli (MVP)"); return {}; } u32 digerOrd = 0; // eğilecek öteki komşu yüz for (u32 ky : g.kenarKomsuYuzler(kenarOrd)) if (ky != yuzOrd) { digerOrd = ky; break; } YuzBilgi yd; if (!digerOrd || !g.yuzBilgi(digerOrd, yd, /*alanHesapla=*/false) || !yd.duzlemMi) { gunluk::uyari("Kenar taşı: kenarın öteki komşusu DÜZ yüz olmalı (MVP)"); return {}; } const Vec3d e = kb.yon.birim(); Vec3d t = ya.normal.birim().capraz(e); // seçili yüz düzlem-içi, kenara dik if (t.uzunlukKare() < 1e-12) { gunluk::uyari("Kenar taşı: kenar/yüz dejenere"); return {}; } t = t.birim(); const Vec3d icA{ya.merkez.x - kb.orta.x, ya.merkez.y - kb.orta.y, ya.merkez.z - kb.orta.z}; if (t.nokta(icA) > 0.0) t = {-t.x, -t.y, -t.z}; // yüz merkezinden UZAĞA = dışarı occtSinyalKur(); try { // Menteşe: öteki yüzün, çekilen kenara paralel ve ondan EN UZAK doğrusal kenarı. const TopoDS_Shape s = tekSolidCoz(sekil(g)); std::vector dyl = ordinalYuzler(s, {digerOrd}); if (dyl.empty()) { gunluk::uyari("Kenar taşı: yüz eşleşmedi"); return {}; } f64 L = 0.0; Vec3d mentese{}; for (TopExp_Explorer ex(dyl[0], TopAbs_EDGE); ex.More(); ex.Next()) { BRepAdaptor_Curve c(TopoDS::Edge(ex.Current())); if (c.GetType() != GeomAbs_Line) continue; const gp_Dir cd = c.Line().Direction(); if (std::abs(cd.X() * e.x + cd.Y() * e.y + cd.Z() * e.z) < 0.999) continue; const gp_Pnt p = c.Value((c.FirstParameter() + c.LastParameter()) * 0.5); Vec3d f{p.X() - kb.orta.x, p.Y() - kb.orta.y, p.Z() - kb.orta.z}; const f64 fe = f.nokta(e); f = {f.x - e.x * fe, f.y - e.y * fe, f.z - e.z * fe}; // kenar eksen bileşenini düş const f64 u = std::sqrt(f.uzunlukKare()); if (u > L + 1e-9) { L = u; mentese = {p.X(), p.Y(), p.Z()}; } } if (L < 1e-6) { gunluk::uyari("Kenar taşı: öteki yüzde çekilen kenara paralel menteşe kenarı yok"); return {}; } // Çekme: menteşeden çekilen kenara doğru, öteki yüz düzleminde (kenara dik bileşen). Vec3d c2{kb.orta.x - mentese.x, kb.orta.y - mentese.y, kb.orta.z - mentese.z}; const f64 ce = c2.nokta(e); c2 = {c2.x - e.x * ce, c2.y - e.y * ce, c2.z - e.z * ce}; if (c2.uzunlukKare() < 1e-12) { gunluk::uyari("Kenar taşı: menteşe dejenere"); return {}; } c2 = c2.birim(); // Hedef: çekilen kenar t×mesafe kadar ötelenmiş (doğrulamada da kullanılır). const Vec3d hedef{kb.orta.x + t.x * mesafe, kb.orta.y + t.y * mesafe, kb.orta.z + t.z * mesafe}; // AÇI (KESİN): menteşe→kenar ile menteşe→hedef arasındaki açı. atan(mesafe/L) yaklaşımı // duvar ZATEN eğikken (ikinci uzatma) sapıyordu → doğrulama düşüyordu ("bir daha aynı // kenara uzatamıyorum" kökü). Bu form her eğimde kenarı TAM hedefe götürür. Vec3d w{hedef.x - mentese.x, hedef.y - mentese.y, hedef.z - mentese.z}; const f64 we = w.nokta(e); w = {w.x - e.x * we, w.y - e.y * we, w.z - e.z * we}; if (w.uzunlukKare() < 1e-12) { gunluk::uyari("Kenar taşı: hedef dejenere"); return {}; } w = w.birim(); const f64 aci = std::acos(std::clamp(c2.nokta(w), -1.0, 1.0)) * 180.0 / std::numbers::pi; if (aci < 1e-4) { gunluk::uyari("Kenar taşı: açı ~0 (mesafe çok küçük)"); return {}; } const f64 tol = std::max(1e-3, std::abs(mesafe) * 0.02); const auto kenarOrada = [&](const BrepGovde& sn) -> bool { if (!sn.doluMu()) return false; for (TopExp_Explorer ex(sekil(sn), TopAbs_EDGE); ex.More(); ex.Next()) { BRepAdaptor_Curve c(TopoDS::Edge(ex.Current())); if (c.GetType() != GeomAbs_Line) continue; const gp_Pnt p = c.Value((c.FirstParameter() + c.LastParameter()) * 0.5); Vec3d f{p.X() - hedef.x, p.Y() - hedef.y, p.Z() - hedef.z}; const f64 fe = f.nokta(e); f = {f.x - e.x * fe, f.y - e.y * fe, f.z - e.z * fe}; if (f.uzunlukKare() < tol * tol) return true; } return false; }; // Bitiriş: koplanar komşuları TEK yüze birleştir (önceki prism/boolean dikişleri de // temizlenir — "uzatılan yüzey ek yapılmış gibi durmasın"). usd başarısız → ham sonuç. const auto bitir = [&](BrepGovde sn) -> BrepGovde { BrepGovde b = usdBirlestir(sn, sekil(sn)); if (b.doluMu()) return b; return sn; }; BrepGovde sn = yuzDraft(g, {digerOrd}, aci, c2, mentese, c2, false); if (kenarOrada(sn)) return bitir(std::move(sn)); sn = yuzDraft(g, {digerOrd}, -aci, c2, mentese, c2, false); if (kenarOrada(sn)) return bitir(std::move(sn)); gunluk::uyari("Kenar taşı: sonuç doğrulanamadı (açı geometriye sığmıyor olabilir)"); return {}; } catch (const Standard_Failure& h) { gunluk::uyari("Kenar taşı istisna: {}", h.what()); return {}; } } BrepGovde yuzDraft(const BrepGovde& g, const std::vector& yuzler, f64 aciDerece, Vec3d cekmeYonu, Vec3d notralNokta, Vec3d notralNormal, bool reflectHat) { if (!g.gecerliMi() || yuzler.empty() || std::abs(aciDerece) < 1e-6) return {}; const f64 dl = std::sqrt(cekmeYonu.x * cekmeYonu.x + cekmeYonu.y * cekmeYonu.y + cekmeYonu.z * cekmeYonu.z); if (dl < 1e-12) return {}; cekmeYonu = {cekmeYonu.x / dl, cekmeYonu.y / dl, cekmeYonu.z / dl}; try { const TopoDS_Shape s = tekSolidCoz(sekil(g)); // compound sarili boolean sonucu (PartDesign) desteklenir std::vector yuzListe = ordinalYuzler(s, yuzler); if (yuzListe.empty()) { gunluk::uyari("Draft: yüz eşleşmedi"); return {}; } const gp_Dir cekme(cekmeYonu.x, cekmeYonu.y, cekmeYonu.z); const f64 rad = aciDerece * std::numbers::pi / 180.0; // Gövde sınır-kutu merkezi (reflect bulunamazsa / geriye-uyum varsayılanı için). gp_Pnt kutuMerkez; { Bnd_Box kutu; BRepBndLib::Add(s, kutu); if (kutu.IsVoid()) return {}; f64 xmin, ymin, zmin, xmax, ymax, zmax; kutu.Get(xmin, ymin, zmin, xmax, ymax, zmax); kutuMerkez = gp_Pnt((xmin + xmax) * 0.5, (ymin + ymax) * 0.5, (zmin + zmax) * 0.5); } // Neutral düzlem (CATIA Neutral Element): notralNormal geçerliyse sahneden seçilen // nötr yüz/düzlem (nokta + normal) kullanılır — yüzler bu düzlemle kesiştikleri hatta // sabit kalır. Aksi halde govde sinir-kutu merkezi + çekme yönü (geriye-uyum varsayılan). const f64 nl = std::sqrt(notralNormal.x * notralNormal.x + notralNormal.y * notralNormal.y + notralNormal.z * notralNormal.z); BRepOffsetAPI_DraftAngle draft(s); if (reflectHat) { // Draft Reflect Line: her yüzün nötr düzlemi kendi yansıma hattı noktasından geçer // (normal = çekme yönü). Yansıma noktası bulunamazsa gövde merkezine düşer. for (const TopoDS_Face& y : yuzListe) { gp_Pnt rp = kutuMerkez; reflectNoktaBul(y, cekme, rp); draft.Add(y, cekme, rad, gp_Pln(rp, cekme)); } } else { gp_Pnt merkez = kutuMerkez; gp_Dir neutralNormal(cekmeYonu.x, cekmeYonu.y, cekmeYonu.z); if (nl > 1e-9) { merkez = gp_Pnt(notralNokta.x, notralNokta.y, notralNokta.z); neutralNormal = gp_Dir(notralNormal.x / nl, notralNormal.y / nl, notralNormal.z / nl); } const gp_Pln neutral(merkez, neutralNormal); for (const TopoDS_Face& y : yuzListe) draft.Add(y, cekme, rad, neutral); } draft.Build(); if (!draft.IsDone()) { gunluk::uyari("Draft başarısız"); return {}; } BrepGovde sonuc = sar(draft.Shape()); ic::renkPropagasyonu(g, draft, sonuc); return sonuc; } catch (const Standard_Failure& h) { gunluk::hata("Draft hatası: {}", h.what()); return {}; } } BrepGovde katiKenarTasi(const BrepGovde& g, u32 kenarOrd, Vec3d vec) { if (!g.gecerliMi() || vec.uzunluk() < 1e-9) return {}; KenarBilgi kb; if (!g.kenarBilgi(kenarOrd, kb)) return {}; Vec3d edir = kb.son - kb.bas; if (edir.uzunluk() < 1e-9) { gunluk::uyari("Kenar taşı: kapalı/dejenere kenar"); return {}; } edir = edir.birim(); const std::vector komsu = g.kenarKomsuYuzler(kenarOrd); if (komsu.size() != 2) { gunluk::uyari("Kenar taşı: kenar 2 yüz paylaşmalı"); return {}; } YuzBilgi yb[2]; for (int i = 0; i < 2; ++i) { if (!g.yuzBilgi(komsu[i], yb[i], false)) return {}; if (!yb[i].duzlemMi) { gunluk::uyari("Kenar taşı MVP: iki komşu yüz de düzlem olmalı"); return {}; } } const Vec3d hedefE = kb.orta + vec; try { const TopoDS_Shape s = tekSolidCoz(sekil(g)); const std::vector yuzListe = ordinalYuzler(s, {komsu[0], komsu[1]}); if (yuzListe.size() != 2) return {}; // Yüz başına menteşe (taşınan kenara ~paralel EN UZAK yüz kenarı) + eğme açısı. gp_Pnt hp[2]; f64 aci[2] = {0, 0}; Vec3d tyon[2]; for (int i = 0; i < 2; ++i) { f64 enUzak = -1.0; Vec3d hOrta{}; for (TopExp_Explorer ex(yuzListe[i], TopAbs_EDGE); ex.More(); ex.Next()) { BRepAdaptor_Curve c(TopoDS::Edge(ex.Current())); const gp_Pnt p0 = c.Value(c.FirstParameter()), p1 = c.Value(c.LastParameter()); Vec3d d{p1.X() - p0.X(), p1.Y() - p0.Y(), p1.Z() - p0.Z()}; if (d.uzunluk() < 1e-9) continue; if (std::abs(d.birim().nokta(edir)) < 0.7) continue; // paralel değil → menteşe olamaz const gp_Pnt pm = c.Value(0.5 * (c.FirstParameter() + c.LastParameter())); const Vec3d m{pm.X(), pm.Y(), pm.Z()}; Vec3d rel = m - kb.orta; rel = rel - edir * rel.nokta(edir); if (rel.uzunluk() > enUzak) { enUzak = rel.uzunluk(); hOrta = m; } } if (enUzak < 1e-6) { gunluk::uyari("Kenar taşı: menteşe bulunamadı"); return {}; } const auto dikBil = [&](const Vec3d& P) { Vec3d r = P - hOrta; return r - edir * r.nokta(edir); }; const Vec3d r0 = dikBil(kb.orta), r1 = dikBil(hedefE); if (r0.uzunluk() < 1e-9 || r1.uzunluk() < 1e-9) continue; // açı tanımsız → yüz atlanır aci[i] = std::acos(std::clamp(r0.birim().nokta(r1.birim()), -1.0, 1.0)); hp[i] = gp_Pnt(hOrta.x, hOrta.y, hOrta.z); tyon[i] = edir.capraz(yb[i].normal); if (tyon[i].uzunluk() < 1e-9) { aci[i] = 0; continue; } tyon[i] = tyon[i].birim(); // yüz düzleminde, menteşeye dik (çekme yönü + nötr normal) } std::vector aktif; for (int i = 0; i < 2; ++i) if (aci[i] > 1e-6) aktif.push_back(i); if (aktif.empty()) { gunluk::uyari("Kenar taşı: eğilecek yüz yok (vec kenara paralel?)"); return {}; } // İşaret DraftAngle sözleşmesinden analitik çıkarılmıyor → dene-ölç: geçerli sonuçlar // içinde taşınan kenarı HEDEFE en yakın götüren kazanır (en çok 4 kombinasyon, feature-time). BrepGovde enIyi; f64 enIyiSapma = 1e30; for (u32 maske = 0; maske < (1u << aktif.size()); ++maske) { try { BRepOffsetAPI_DraftAngle draft(s); bool addOk = true; for (size_t j = 0; j < aktif.size(); ++j) { const int i = aktif[j]; const f64 a = ((maske >> j) & 1) ? -aci[i] : aci[i]; const gp_Dir di(tyon[i].x, tyon[i].y, tyon[i].z); draft.Add(yuzListe[i], di, a, gp_Pln(hp[i], di)); if (!draft.AddDone()) { addOk = false; break; } } if (!addOk) continue; draft.Build(); if (!draft.IsDone()) continue; BrepGovde aday = sar(draft.Shape()); if (!aday.gecerliMi()) continue; if (topolojiOzeti(aday).katiSayi < 1 || aday.hacim() < 1e-6) continue; // KATI kalmalı f64 sap = 1e30; // sonuç kenar ortalarının hedefe en yakını const int ke = topolojiOzeti(aday).kenarSayi; for (int e = 1; e <= ke; ++e) { KenarBilgi kk; if (aday.kenarBilgi((u32)e, kk)) sap = std::min(sap, (kk.orta - hedefE).uzunluk()); } if (sap < enIyiSapma) { enIyiSapma = sap; enIyi = std::move(aday); } } catch (const Standard_Failure&) { continue; } } if (!enIyi.gecerliMi() || enIyiSapma > vec.uzunluk() * 0.6 + 1e-3) { gunluk::uyari("Kenar taşı başarısız (draft çözülemedi / hedeften sapma büyük)"); return {}; } return enIyi; } catch (const Standard_Failure& h) { gunluk::hata("Kati kenar tasi hatasi: {}", h.what()); return {}; } } // ---- Sew Surface / Replace Face ortak: yüzeyle katıyı böl, bir tarafı tut ---- BrepGovde yuzeyDik(const BrepGovde& kati, const BrepGovde& yuzey, Vec3d yon, bool ters) { if (!kati.gecerliMi() || !yuzey.gecerliMi()) return {}; const f64 dl = std::sqrt(yon.x * yon.x + yon.y * yon.y + yon.z * yon.z); if (dl < 1e-12) return {}; yon = {yon.x / dl, yon.y / dl, yon.z / dl}; try { const TopoDS_Shape sk = sekil(kati); const TopoDS_Shape sy = sekil(yuzey); // Splitter: katıyı yüzey (araç) boyunca ayrık katı parçalara böl (General Fuse tabanlı). BRepAlgoAPI_Splitter bol; TopTools_ListOfShape args, tools; args.Append(sk); tools.Append(sy); bol.SetArguments(args); bol.SetTools(tools); bol.Build(); if (!bol.IsDone()) { gunluk::uyari("Yüzey böl: splitter başarısız"); return {}; } // Referans nokta: yüzeyin sınır-kutu merkezi (parça hangi tarafta → yon·(merkez−ref)). Bnd_Box yb; BRepBndLib::Add(sy, yb); if (yb.IsVoid()) return {}; f64 xmin, ymin, zmin, xmax, ymax, zmax; yb.Get(xmin, ymin, zmin, xmax, ymax, zmax); const gp_Pnt ref((xmin + xmax) * 0.5, (ymin + ymax) * 0.5, (zmin + zmax) * 0.5); // Sonuç katı parçalarından yon tarafındakileri (ters → diğer taraf) fuse et. BrepGovde sonuc; bool ilk = true; for (TopExp_Explorer e(bol.Shape(), TopAbs_SOLID); e.More(); e.Next()) { const TopoDS_Solid katiParca = TopoDS::Solid(e.Current()); GProp_GProps oz; BRepGProp::VolumeProperties(katiParca, oz); const gp_Pnt c = oz.CentreOfMass(); const f64 d = (c.X() - ref.X()) * yon.x + (c.Y() - ref.Y()) * yon.y + (c.Z() - ref.Z()) * yon.z; const bool tut = ters ? (d < 0.0) : (d > 0.0); if (!tut) continue; BrepGovde parca = sar(katiParca); if (ilk) { sonuc = std::move(parca); ilk = false; } else { BrepGovde b = birlesim(sonuc, parca); if (b.gecerliMi()) sonuc = std::move(b); } } if (!sonuc.doluMu()) { gunluk::uyari("Yüzey böl: tutulan parça yok (yüzey katıyı tam kesmiyor olabilir)"); return {}; } return sonuc; } catch (const Standard_Failure& h) { gunluk::hata("Yüzey böl hatası: {}", h.what()); return {}; } } BrepGovde duzlemleBol(const BrepGovde& g, Vec3d duzlemNokta, Vec3d duzlemNormal, bool ters) { const f64 nlen = std::sqrt(duzlemNormal.x * duzlemNormal.x + duzlemNormal.y * duzlemNormal.y + duzlemNormal.z * duzlemNormal.z); if (!g.gecerliMi() || nlen < 1e-9) return {}; duzlemNormal = duzlemNormal * (1.0 / nlen); occtSinyalKur(); try { // Kesici düzlem yüzü katıyı TAM kesecek boyutta (bbox köşegeni — cikintiTrsfUygula kalıbı); // düzlem UV merkezi = duzlemNokta → yuzeyDik'in referans noktası (yüzey bbox merkezi) budur. const TopoDS_Shape s = tekSolidCoz(sekil(g)); Bnd_Box bb; BRepBndLib::Add(s, bb); if (bb.IsVoid()) return {}; f64 x0, y0, z0, x1, y1, z1; bb.Get(x0, y0, z0, x1, y1, z1); const f64 diag = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0) + (z1 - z0) * (z1 - z0)) + 1.0; const gp_Pln pln(gp_Pnt(duzlemNokta.x, duzlemNokta.y, duzlemNokta.z), gp_Dir(duzlemNormal.x, duzlemNormal.y, duzlemNormal.z)); const TopoDS_Face duzlemYuz = BRepBuilderAPI_MakeFace(pln, -diag, diag, -diag, diag).Face(); // Sözleşme: NORMAL yönündeki yarı ATILIR → tutulan taraf = −normal (ters → diğer yarı). return yuzeyDik(g, sar(duzlemYuz), duzlemNormal * -1.0, ters); } catch (const Standard_Failure& h) { gunluk::uyari("Düzlemle böl istisna: {}", h.what()); return {}; } } // ---- Part Design: Dizi (Pattern) & Aynalama (Mirror) ------------------- // Hedef katiyi tumuyle (kenar/yuz secimi YOK) rijit donusumle cogaltir; kopyalar // birlesim (Fuse) ile toplanir. Sonuc orijinali de icerir (ilk kopya = birim donusum). BrepGovde diziDik(const BrepGovde& hedef, Vec3d yon1, f64 adim1, u32 adet1, Vec3d yon2, f64 adim2, u32 adet2) { if (!hedef.gecerliMi()) return {}; if (adet1 < 1) adet1 = 1; if (adet2 < 1) adet2 = 1; yon1 = yon1.birim(); yon2 = yon2.birim(); BrepGovde sonuc; bool ilk = true; for (u32 j = 0; j < adet2; ++j) { for (u32 i = 0; i < adet1; ++i) { Donusumd d; // sadece oteleme (yonelim birim, olcek 1) d.konum = yon1 * (adim1 * (f64)i) + yon2 * (adim2 * (f64)j); BrepGovde kopya = donusumUygula(hedef, d); if (!kopya.gecerliMi()) continue; if (ilk) { sonuc = std::move(kopya); ilk = false; continue; } BrepGovde b = birlesim(sonuc, kopya); if (b.gecerliMi()) sonuc = std::move(b); // fuse basarisiz → onceki birikimi koru } } return sonuc; } BrepGovde diziDairesel(const BrepGovde& hedef, Vec3d eksenNokta, Vec3d eksenYon, f64 toplamAci, u32 adet) { if (!hedef.gecerliMi() || adet < 1) return {}; const Vec3d eks = eksenYon.birim(); if (eks.uzunlukKare() < 1e-18) return {}; const f64 adimAci = (toplamAci / (f64)adet) * std::numbers::pi / 180.0; // radyan/kopya BrepGovde sonuc; bool ilk = true; for (u32 i = 0; i < adet; ++i) { Donusumd d; d.yonelim = Quatd::eksenAci(eks, adimAci * (f64)i); // eksenNokta etrafinda don: p' = R*(p - P) + P → konum = P - R*P. d.konum = eksenNokta - d.yonelim.dondur(eksenNokta); BrepGovde kopya = donusumUygula(hedef, d); if (!kopya.gecerliMi()) continue; if (ilk) { sonuc = std::move(kopya); ilk = false; continue; } BrepGovde b = birlesim(sonuc, kopya); if (b.gecerliMi()) sonuc = std::move(b); } return sonuc; } BrepGovde diziKonum(const BrepGovde& hedef, const std::vector& ofsetler) { if (!hedef.gecerliMi() || ofsetler.empty()) return {}; BrepGovde sonuc; bool ilk = true; for (const Vec3d& o : ofsetler) { Donusumd d; // sadece oteleme (yonelim birim, olcek 1) — User Pattern nokta konumu d.konum = o; BrepGovde kopya = donusumUygula(hedef, d); if (!kopya.gecerliMi()) continue; if (ilk) { sonuc = std::move(kopya); ilk = false; continue; } BrepGovde b = birlesim(sonuc, kopya); if (b.gecerliMi()) sonuc = std::move(b); // fuse basarisiz → onceki birikimi koru } return sonuc; } BrepGovde simetrik(const BrepGovde& hedef, Vec3d refNokta, Vec3d refYon, int refTip, bool birlestir) { if (!hedef.gecerliMi()) return {}; const f64 yl = std::sqrt(refYon.x * refYon.x + refYon.y * refYon.y + refYon.z * refYon.z); if (refTip != 2 && yl < 1e-12) return {}; // duzlem/dogru: yon zorunlu try { gp_Trsf t; const gp_Pnt P(refNokta.x, refNokta.y, refNokta.z); if (refTip == 2) { t.SetMirror(P); // nokta simetrisi } else { const gp_Dir D(refYon.x / yl, refYon.y / yl, refYon.z / yl); if (refTip == 1) t.SetMirror(gp_Ax1(P, D)); // eksen (dogru) etrafinda 180° else t.SetMirror(gp_Ax2(P, D)); // duzleme gore ayna (Ax2 ana yonu = normal) } BrepGovde sim = sar(BRepBuilderAPI_Transform(sekil(hedef), t, true).Shape()); if (!sim.gecerliMi()) return {}; if (!birlestir) return sim; // Symmetry: yalniz tasinmis kopya BrepGovde b = birlesim(hedef, sim); // Mirror: orijinal + ayna tek kati return b.gecerliMi() ? std::move(b) : std::move(sim); } catch (const Standard_Failure& h) { gunluk::hata("Simetri hatasi: {}", h.what()); return {}; } } BrepGovde aynala(const BrepGovde& hedef, Vec3d duzlemNokta, Vec3d duzlemNormal) { return simetrik(hedef, duzlemNokta, duzlemNormal, /*refTip=duzlem*/ 0, /*birlestir*/ true); } BrepGovde olcekle(const BrepGovde& hedef, Vec3d merkez, f64 oran) { if (!hedef.gecerliMi() || std::abs(oran) < 1e-9) return {}; try { gp_Trsf t; // merkez etrafinda tekduze olcek: X' = merkez + oran*(X - merkez) t.SetScale(gp_Pnt(merkez.x, merkez.y, merkez.z), oran); BrepGovde sonuc = sar(BRepBuilderAPI_Transform(sekil(hedef), t, true).Shape()); if (!sonuc.gecerliMi()) return {}; if (hedef.yuzRenkliMi()) sonuc.yuzRenkleriAta(hedef.yuzRenkleri()); // TopExp sirasi korunur return sonuc; } catch (const Standard_Failure& h) { gunluk::hata("Olcekleme hatasi: {}", h.what()); return {}; } } // ---- Part Design: Prizma (Pad / Pocket tabani) ------------------------- namespace { // Kapali polyline (dunya-uzayi) → TopoDS_Wire. Ardisik noktalar arasi kenar, // son nokta ilkine baglanir. Gecersiz (nokta<3) → bos wire (IsNull). TopoDS_Wire konturWire(const std::vector& kontur) { if (kontur.size() < 3) return {}; BRepBuilderAPI_MakePolygon poli; for (const Vec3d& p : kontur) poli.Add(gp_Pnt(p.x, p.y, p.z)); poli.Close(); if (!poli.IsDone()) return {}; return poli.Wire(); } // Dis wire + delik wire'lari → planar TopoDS_Face. Delik wire'lari giris sirasina gore // dis ile ayni yonde gelebilir; ShapeFix_Face::FixOrientation() delik/dis winding'i // yuz normaline gore duzeltir (yoksa delik "dolu ada" kalir → yanlis dolu katı). // Poligon (konturWire) ve gerçek-geometri (egriKonturWire) yüzleri için ORTAK. TopoDS_Face yuzKur(const TopoDS_Wire& dis, const std::vector& delikler) { if (dis.IsNull()) return {}; BRepBuilderAPI_MakeFace yuzYap(dis); for (const TopoDS_Wire& d : delikler) if (!d.IsNull()) yuzYap.Add(d); if (!yuzYap.IsDone()) return {}; TopoDS_Face yuz = yuzYap.Face(); if (!delikler.empty()) { // delik oryantasyonlarini garantiye al ShapeFix_Face fix(yuz); fix.FixOrientation(); yuz = fix.Face(); } return yuz; } // Poligon konturlarindan (dis=0, gerisi delik) yüz. TopoDS_Face yuzDelikli(const std::vector>& konturlar) { if (konturlar.empty()) return {}; std::vector delikler; for (size_t i = 1; i < konturlar.size(); ++i) delikler.push_back(konturWire(konturlar[i])); return yuzKur(konturWire(konturlar[0]), delikler); } // Kurulmuş yüzü yon (birim) boyunca ileri/geri extrude → katı. draftAci≠0 ise yan yüzleri // neutralNokta düzleminden itibaren eğer (kalıp çıkış açısı). prizmaCift + prizmaCiftEgri // ORTAK extrude çekirdeği (poligon/gerçek-geometri yüz farkı çağırana ait). BrepGovde prizmaYuzden(TopoDS_Shape yuz, const Vec3d& yon, f64 ileri, f64 geri, f64 draftAci, const Vec3d& neutralNokta) { const f64 toplam = ileri + geri; if (yuz.IsNull() || toplam <= 1e-9) return {}; try { // Profili -yon*geri kaydır (geri limit başlangıcı), toplam=ileri+geri uzat. if (geri > 1e-12) { gp_Trsf kaydir; kaydir.SetTranslation(gp_Vec(-yon.x * geri, -yon.y * geri, -yon.z * geri)); yuz = BRepBuilderAPI_Transform(yuz, kaydir).Shape(); } const gp_Vec itki(yon.x * toplam, yon.y * toplam, yon.z * toplam); BRepPrimAPI_MakePrism kati(yuz, itki); kati.Build(); if (!kati.IsDone()) { gunluk::uyari("Prizma: cikinti basarisiz"); return {}; } TopoDS_Shape sonuc = kati.Shape(); if (std::abs(draftAci) > 1e-6) { try { const gp_Dir cekme(yon.x, yon.y, yon.z); const gp_Pln neutral(gp_Pnt(neutralNokta.x, neutralNokta.y, neutralNokta.z), cekme); BRepOffsetAPI_DraftAngle draft(sonuc); const f64 rad = draftAci * 3.14159265358979323846 / 180.0; for (TopExp_Explorer ex(sonuc, TopAbs_FACE); ex.More(); ex.Next()) { const TopoDS_Face f = TopoDS::Face(ex.Current()); BRepAdaptor_Surface s(f); if (s.GetType() != GeomAbs_Plane) continue; const gp_Dir fn = s.Plane().Axis().Direction(); if (std::abs(fn.Dot(cekme)) < 0.3) // yan yüz: normali çekmeye ~dik draft.Add(f, cekme, rad, neutral); } draft.Build(); if (draft.IsDone()) sonuc = draft.Shape(); else gunluk::uyari("Draft uygulanamadı → düz prizma"); } catch (const Standard_Failure& h) { gunluk::uyari("Draft hatası ({}) → düz prizma", h.what()); } } return sar(sonuc); } catch (const Standard_Failure& h) { gunluk::hata("Prizma hatasi: {}", h.what()); return {}; } } } // namespace BrepGovde prizmaCift(const std::vector>& konturlar, Vec3d yon, f64 ileri, f64 geri, f64 draftAci) { if (konturlar.empty() || ileri + geri <= 1e-9) return {}; const f64 uzunluk = std::sqrt(yon.x * yon.x + yon.y * yon.y + yon.z * yon.z); if (uzunluk < 1e-12) return {}; yon = {yon.x / uzunluk, yon.y / uzunluk, yon.z / uzunluk}; const TopoDS_Face yuz = yuzDelikli(konturlar); // dis + delikler, winding duzeltmeli if (yuz.IsNull()) { gunluk::uyari("Prizma: yuz kurulamadi"); return {}; } const Vec3d neutral = konturlar[0].empty() ? Vec3d{} : konturlar[0][0]; return prizmaYuzden(yuz, yon, ileri, geri, draftAci, neutral); } // prizma() = prizmaCift'in özel hali: simetrik → yarı-yarı çift yön; değilse tek yön. BrepGovde prizma(const std::vector>& konturlar, Vec3d yon, f64 derinlik, bool simetrik) { if (derinlik <= 1e-9) return {}; return simetrik ? prizmaCift(konturlar, yon, derinlik / 2, derinlik / 2) : prizmaCift(konturlar, yon, derinlik, 0.0); } namespace { // 2B ray-casting nokta-in-polygon (coplanar profil düzlem uzayına projekte edilmiş uv). // Kenardaki eşitlik durumları even-odd testinde önemsiz (konturlar kesişmez). bool noktaPoligonIcinde(const Vec2d& pt, const std::vector& poly) { bool ic = false; const size_t n = poly.size(); if (n < 3) return false; for (size_t i = 0, j = n - 1; i < n; j = i++) { const Vec2d& a = poly[i]; const Vec2d& b = poly[j]; if ((a.y > pt.y) != (b.y > pt.y)) { const f64 xkes = (b.x - a.x) * (pt.y - a.y) / (b.y - a.y) + a.x; if (pt.x < xkes) ic = !ic; } } return ic; } // even-odd domain gruplaması: örnek konturlardan (Vec3d, coplanar → yon düzlemine projekte) // iç-içelik derinliği hesaplanır (çift=DIŞ sınır, tek=DELİK). Her domain = {dış indeks, // immediate delik indeksleri...}, [0]=dış. Hem prizmaMulti hem prizmaMultiEgri kullanır. std::vector> domainGrupla(const std::vector>& ornek, const Vec3d& yon) { std::vector> cikti; const f64 uzun = std::sqrt(yon.x * yon.x + yon.y * yon.y + yon.z * yon.z); if (uzun < 1e-12) return cikti; const Vec3d n{yon.x / uzun, yon.y / uzun, yon.z / uzun}; // profil düzlem normali const Vec3d ref = (std::abs(n.x) < 0.9) ? Vec3d{1, 0, 0} : Vec3d{0, 1, 0}; const Vec3d u = n.capraz(ref).birim(); const Vec3d v = n.capraz(u).birim(); const auto pro = [&](const Vec3d& p) -> Vec2d { return {p.nokta(u), p.nokta(v)}; }; const size_t K = ornek.size(); std::vector> poly2b(K); for (size_t i = 0; i < K; ++i) { poly2b[i].reserve(ornek[i].size()); for (const Vec3d& p : ornek[i]) poly2b[i].push_back(pro(p)); } // derinlik[i] = i'yi içeren kontur sayısı. 3 örnek noktayla oy (sınır köşesi belirsizliği). std::vector derinlik(K, 0); std::vector> icinde(K, std::vector(K, false)); for (size_t i = 0; i < K; ++i) { const std::vector& pi = poly2b[i]; if (pi.size() < 3) continue; const Vec2d ornekN[3] = {pi[0], pi[pi.size() / 3], pi[2 * pi.size() / 3]}; for (size_t j = 0; j < K; ++j) { if (i == j || poly2b[j].size() < 3) continue; int oy = 0; for (const Vec2d& t : ornekN) if (noktaPoligonIcinde(t, poly2b[j])) oy++; if (oy >= 2) { icinde[i][j] = true; derinlik[i]++; } } } std::vector disIndeks; // domain sırası ↔ kontur indeksi for (size_t i = 0; i < K; ++i) { if (poly2b[i].size() < 3) continue; if (derinlik[i] % 2 == 0) { cikti.push_back({(int)i}); disIndeks.push_back((int)i); } } for (size_t h = 0; h < K; ++h) { if (poly2b[h].size() < 3 || derinlik[h] % 2 == 0) continue; // yalnız delikler int enIyi = -1, enDerin = -1; // h'yi içeren immediate (en derin) dış kontur for (size_t d = 0; d < disIndeks.size(); ++d) { const int o = disIndeks[d]; if (icinde[h][o] && derinlik[o] > enDerin) { enDerin = derinlik[o]; enIyi = (int)d; } } if (enIyi >= 0) cikti[enIyi].push_back((int)h); } return cikti; } // n parçayı parcaUret(i) ile üretip birlesim (fuse) ile tek katıya toplar. Geçersiz parça // atlanır; fuse başarısız → önceki korunur; istisna → geçersiz. prizmaMulti + prizmaMultiEgri. template BrepGovde fuseParcalar(size_t n, F parcaUret, const char* etiket) { BrepGovde sonuc; bool ilk = true; for (size_t i = 0; i < n; ++i) { BrepGovde parca = parcaUret(i); if (!parca.gecerliMi()) continue; if (ilk) { sonuc = std::move(parca); ilk = false; continue; } try { BrepGovde b = birlesim(sonuc, parca); if (!b.gecerliMi()) { gunluk::uyari("{}: fuse başarısız → önceki korunuyor", etiket); continue; } sonuc = std::move(b); } catch (const Standard_Failure& h) { gunluk::hata("{} fuse hatası: {}", etiket, h.what()); return {}; } } return sonuc; } } // namespace BrepGovde prizmaMulti(const std::vector>& konturlar, Vec3d yon, f64 ileri, f64 geri, f64 draftAci) { if (konturlar.empty()) return {}; // Tek kontur → prizmaCift ile birebir (geriye-uyum: even-odd gereksiz). if (konturlar.size() == 1) return prizmaCift(konturlar, yon, ileri, geri, draftAci); const auto domainler = domainGrupla(konturlar, yon); if (domainler.empty()) return {}; return fuseParcalar(domainler.size(), [&](size_t d) { std::vector> parcaKontur; parcaKontur.reserve(domainler[d].size()); for (int idx : domainler[d]) parcaKontur.push_back(konturlar[idx]); return prizmaCift(parcaKontur, yon, ileri, geri, draftAci); }, "prizmaMulti"); } namespace { // Kapali konturlardan (dis + delikler) planar TopoDS_Face kurar. Bos → IsNull. TopoDS_Face konturYuz(const std::vector>& konturlar) { return yuzDelikli(konturlar); // winding-duzeltmeli ortak kurulum } // ---- Gerçek-geometri (BREP-doğru) profil yardımcıları ---- // Bir ProfilKontur (kapalı döngü) → gerçek edge'li TopoDS_Wire. Çember/Elips tek kapalı // edge; Cizgi/Yay uç-uca zincir. Daire → gerçek silindir yüzeyi (facet yok). TopoDS_Wire egriKonturWire(const geometri::ProfilKontur& kontur) { if (kontur.empty()) return {}; using Tip = geometri::ProfilEgri::Tip; try { // Tek kapalı eğri (Çember/Elips) → doğrudan. if (kontur.size() == 1 && (kontur[0].tip == Tip::Cember || kontur[0].tip == Tip::Elips)) { const geometri::ProfilEgri& e = kontur[0]; const gp_Pnt mrk(e.merkez.x, e.merkez.y, e.merkez.z); const gp_Dir eks(e.eksen.x, e.eksen.y, e.eksen.z); const gp_Dir rx(e.refX.x, e.refX.y, e.refX.z); TopoDS_Edge ke; if (e.tip == Tip::Cember) ke = BRepBuilderAPI_MakeEdge(gp_Circ(gp_Ax2(mrk, eks, rx), e.yaricap)).Edge(); else ke = BRepBuilderAPI_MakeEdge(gp_Elips(gp_Ax2(mrk, eks, rx), e.yaricap, e.kucukYaricap)).Edge(); if (ke.IsNull()) { gunluk::uyari("egriKonturWire: tek-kapalı (çember/elips) edge null"); return {}; } return BRepBuilderAPI_MakeWire(ke).Wire(); } // Tümü düz çizgi mi? → MakePolygon (tek nokta dizisi; komşu köşe vertex paylaşımı // garantili). Düz kenar zaten gerçek edge (tessellation değil) → BREP-doğru + sağlam. bool hepsiCizgi = true; for (const geometri::ProfilEgri& e : kontur) if (e.tip != Tip::Cizgi) { hepsiCizgi = false; break; } if (hepsiCizgi) { BRepBuilderAPI_MakePolygon poli; for (const geometri::ProfilEgri& e : kontur) poli.Add(gp_Pnt(e.p0.x, e.p0.y, e.p0.z)); poli.Close(); if (!poli.IsDone()) { gunluk::uyari("egriKonturWire: MakePolygon başarısız ({} çizgi)", kontur.size()); return {}; } return poli.Wire(); } // Yay/eliptik-yay içeren karışık zincir → edge'leri ShapeFix_Wire ile TOLERANSLI birleştir. // Trim/kesişim uçlarındaki mikro-boşluklar (sketchProfilleri 1e-4 kapalı sayar ama MakeWire // 1e-7 bağlayamaz) burada kapatılır: FixReorder (sırala) + FixConnected (uçları birleştir) + // FixClosed (kapat). Kare+daire kes/kırp gibi karışık profiller artık Pad'lenir. Handle(ShapeExtend_WireData) wd = new ShapeExtend_WireData(); for (const geometri::ProfilEgri& e : kontur) { TopoDS_Edge ke; // gp_Dir (eks/rx) YALNIZ yay dallarında kurulur — çizgi eğrisinin eksen/refX'i (0,0,0) // olduğundan gp_Dir "zero norm" istisnası atıp TÜM wire'ı çökertiyordu. if (e.tip == Tip::Yay && e.yaricap > 1e-9) { // Yay uçları GERÇEK profil-kavşak noktaları (p0/p1); açılar (aci0/aci1) solver // sonrası STALE olabilir (solver g.a/g.b noktalarını taşır ama açıları güncellemez) // → yayı 3-NOKTADAN kur: uçlar p0/p1 ile BİREBİR (komşu çizgilerle tam bağ). Orta // nokta yalnız yayın hangi yöne şiştiğini seçer. Böylece ShapeFix'in uç-kaydırması // (Pad'i profilden kaydırıp uzatan sebep) ortadan kalkar. const Vec3d ry = e.eksen.capraz(e.refX).birim(); f64 a0 = e.aci0, a1 = e.aci1; if (a1 <= a0) a1 += 2.0 * std::numbers::pi; const f64 am = 0.5 * (a0 + a1), ca = std::cos(am), sa = std::sin(am); const gp_Pnt pm(e.merkez.x + e.yaricap * (ca * e.refX.x + sa * ry.x), e.merkez.y + e.yaricap * (ca * e.refX.y + sa * ry.y), e.merkez.z + e.yaricap * (ca * e.refX.z + sa * ry.z)); GC_MakeArcOfCircle mk(gp_Pnt(e.p0.x, e.p0.y, e.p0.z), pm, gp_Pnt(e.p1.x, e.p1.y, e.p1.z)); if (mk.IsDone()) ke = BRepBuilderAPI_MakeEdge(mk.Value()).Edge(); else ke = BRepBuilderAPI_MakeEdge(gp_Pnt(e.p0.x, e.p0.y, e.p0.z), gp_Pnt(e.p1.x, e.p1.y, e.p1.z)).Edge(); // düz fallback } else if (e.tip == Tip::EliptikYay && e.yaricap > 1e-9 && e.kucukYaricap > 1e-9) { const gp_Pnt mrk(e.merkez.x, e.merkez.y, e.merkez.z); const gp_Dir eks(e.eksen.x, e.eksen.y, e.eksen.z); const gp_Dir rx(e.refX.x, e.refX.y, e.refX.z); ke = BRepBuilderAPI_MakeEdge(gp_Elips(gp_Ax2(mrk, eks, rx), e.yaricap, e.kucukYaricap), e.aci0, e.aci1).Edge(); } else if (e.tip == Tip::Spline && e.noktalar.size() >= 2) { // Spline: gecis noktalarindan INTERPOLASYON (GeomAPI_Interpolate — noktalardan // TAM gecer; PointsToBSpline yaklastirir, uc sadakati bozulurdu — Yay'daki // stale-aci dersi: uclar komsu kenarlarla birebir olmali, ShapeFix kaydirmasin). Handle(TColgp_HArray1OfPnt) dizi = new TColgp_HArray1OfPnt(1, (int)e.noktalar.size()); for (int i = 0; i < (int)e.noktalar.size(); ++i) dizi->SetValue(i + 1, gp_Pnt(e.noktalar[(size_t)i].x, e.noktalar[(size_t)i].y, e.noktalar[(size_t)i].z)); GeomAPI_Interpolate mk(dizi, /*periyodik*/ false, 1e-7); mk.Perform(); if (mk.IsDone()) ke = BRepBuilderAPI_MakeEdge(mk.Curve()).Edge(); else ke = BRepBuilderAPI_MakeEdge(gp_Pnt(e.p0.x, e.p0.y, e.p0.z), gp_Pnt(e.p1.x, e.p1.y, e.p1.z)).Edge(); // düz fallback } else { // Cizgi (ve güvenli fallback): sadece uç noktalar const gp_Pnt a(e.p0.x, e.p0.y, e.p0.z), b(e.p1.x, e.p1.y, e.p1.z); ke = BRepBuilderAPI_MakeEdge(a, b).Edge(); } if (!ke.IsNull()) wd->Add(ke); else gunluk::uyari("egriKonturWire: edge null (tip={}, r={})", (int)e.tip, e.yaricap); } if (wd->NbEdges() == 0) { gunluk::uyari("egriKonturWire: hiç edge kurulamadı ({} eğri)", kontur.size()); return {}; } ShapeFix_Wire fix; fix.Load(wd); fix.SetPrecision(1e-3); fix.SetMaxTolerance(1e-2); // 1e-1 wire'ı düzlemden saptırıp MakeFace'i bozuyordu fix.FixReorder(); fix.FixConnected(); fix.FixClosed(); const TopoDS_Wire w = fix.Wire(); if (w.IsNull() || !w.Closed()) gunluk::uyari("egriKonturWire: karışık zincir kapatılamadı ({} edge)", wd->NbEdges()); if (w.IsNull()) return {}; return w; } catch (const Standard_Failure& h) { gunluk::uyari("egriKonturWire istisna: {}", h.what()); return {}; } } // ProfilKontur → even-odd analizi için düzlem-içi örnek noktalar (dünya uzayı). std::vector egriOrnekle(const geometri::ProfilKontur& kontur) { using Tip = geometri::ProfilEgri::Tip; std::vector pts; const auto nokta = [](const Vec3d& m, const Vec3d& rx, const Vec3d& ry, f64 ca, f64 sb) { return Vec3d{m.x + rx.x * ca + ry.x * sb, m.y + rx.y * ca + ry.y * sb, m.z + rx.z * ca + ry.z * sb}; }; for (const geometri::ProfilEgri& e : kontur) { if (e.tip == Tip::Cizgi) { pts.push_back(e.p0); continue; } // p1 = sonraki p0 if (e.tip == Tip::Spline) { // gecis noktalari (son haric — p1 = sonraki p0) // NOT: ry hesabından ÖNCE dallan — spline'ın eksen/refX'i (0,0,0), birim() // sıfır-norm verirdi (egriKonturWire'daki gp_Dir dersinin aynısı). for (size_t i = 0; i + 1 < e.noktalar.size(); ++i) pts.push_back(e.noktalar[i]); continue; } const Vec3d ry = e.eksen.capraz(e.refX).birim(); if (e.tip == Tip::Yay || e.tip == Tip::EliptikYay) { // açık yay → aralık, p1 hariç const f64 a = e.yaricap, b = (e.tip == Tip::EliptikYay ? e.kucukYaricap : e.yaricap); const int n = 16; for (int i = 0; i < n; ++i) { const f64 t = e.aci0 + (e.aci1 - e.aci0) * (f64)i / (f64)n; pts.push_back(nokta(e.merkez, e.refX, ry, a * std::cos(t), b * std::sin(t))); } } else { // Cember / Elips → tam tur const f64 a = e.yaricap, b = (e.tip == Tip::Elips ? e.kucukYaricap : e.yaricap); const int n = 48; for (int i = 0; i < n; ++i) { const f64 t = 2.0 * std::numbers::pi * (f64)i / (f64)n; pts.push_back(nokta(e.merkez, e.refX, ry, a * std::cos(t), b * std::sin(t))); } } } return pts; } // ProfilKontur listesi (dis=0, gerisi delik) → gerçek-geometri planar yüz. Yüz AÇIK düzlemle // (profil normali + nokta) kurulur → karışık (çizgi+yay, trim/kes-kırp) wire'da MakeFace'in // düzlemi otomatik bulamaması engellenir. Başarısızsa wire-otomatik yüze düşer. TopoDS_Face egriYuzDelikli(const std::vector& konturlar) { if (konturlar.empty() || konturlar[0].empty()) return {}; const TopoDS_Wire dis = egriKonturWire(konturlar[0]); if (dis.IsNull()) { gunluk::uyari("egriYuzDelikli: dış wire kurulamadı"); return {}; } // Profil düzlemi: ilk eğrinin normal + bir nokta (çizgi p0 / eğri merkez). const geometri::ProfilEgri& e0 = konturlar[0][0]; const Vec3d nk = (e0.tip == geometri::ProfilEgri::Tip::Cizgi) ? e0.p0 : e0.merkez; const Vec3d nrm = e0.eksen; TopoDS_Face yuz; try { if (std::abs(nrm.x) + std::abs(nrm.y) + std::abs(nrm.z) > 1e-9) { const gp_Pln pln(gp_Pnt(nk.x, nk.y, nk.z), gp_Dir(nrm.x, nrm.y, nrm.z)); BRepBuilderAPI_MakeFace mf(pln, dis, /*Inside*/ Standard_True); if (mf.IsDone()) yuz = mf.Face(); } if (yuz.IsNull()) { // düzlem-açık başarısız → wire'dan otomatik düzlem BRepBuilderAPI_MakeFace mf(dis); if (mf.IsDone()) yuz = mf.Face(); } if (yuz.IsNull()) { gunluk::uyari("egriYuzDelikli: MakeFace başarısız (profil düzlemsel/kapalı değil?)"); return {}; } // Delikler (iç konturlar) + winding düzeltmesi. bool delikVar = false; for (size_t i = 1; i < konturlar.size(); ++i) { const TopoDS_Wire d = egriKonturWire(konturlar[i]); if (d.IsNull()) continue; BRepBuilderAPI_MakeFace mf(yuz); mf.Add(d); if (mf.IsDone()) { yuz = mf.Face(); delikVar = true; } } if (delikVar) { ShapeFix_Face fix(yuz); fix.FixOrientation(); yuz = fix.Face(); } } catch (const Standard_Failure& h) { gunluk::uyari("egriYuzDelikli hatası: {}", h.what()); return {}; } return yuz; } // prizmaCift'in gerçek-geometri sürümü: tek domain (dis + delikler) → gerçek yüz → extrude. BrepGovde prizmaCiftEgri(const std::vector& konturlar, Vec3d yon, f64 ileri, f64 geri, f64 draftAci) { if (konturlar.empty() || ileri + geri <= 1e-9) return {}; const f64 uzunluk = std::sqrt(yon.x * yon.x + yon.y * yon.y + yon.z * yon.z); if (uzunluk < 1e-12) return {}; yon = {yon.x / uzunluk, yon.y / uzunluk, yon.z / uzunluk}; const TopoDS_Face yuz = egriYuzDelikli(konturlar); if (yuz.IsNull()) { gunluk::uyari("PrizmaEgri: yuz kurulamadi"); return {}; } Vec3d neutral{}; // draft neutral düzlem noktası (profil düzleminde) if (!konturlar[0].empty()) { const geometri::ProfilEgri& e0 = konturlar[0][0]; neutral = (e0.tip == geometri::ProfilEgri::Tip::Cizgi) ? e0.p0 : e0.merkez; } return prizmaYuzden(yuz, yon, ileri, geri, draftAci, neutral); } // Kurulmuş yüzü eksen (eksenNokta + eksenYon) etrafında aciDerece döndür → KATI. dondurKati // + dondurKatiEgri ORTAK revol çekirdeği (poligon/gerçek-geometri yüz farkı çağırana ait). BrepGovde revolYuzden(const TopoDS_Face& yuz, Vec3d eksenNokta, Vec3d eksenYon, f64 aciDerece) { if (yuz.IsNull() || aciDerece <= 1e-6) return {}; const f64 en = std::sqrt(eksenYon.x * eksenYon.x + eksenYon.y * eksenYon.y + eksenYon.z * eksenYon.z); if (en < 1e-12) return {}; eksenYon = {eksenYon.x / en, eksenYon.y / en, eksenYon.z / en}; if (aciDerece > 360.0) aciDerece = 360.0; try { const gp_Ax1 eksen(gp_Pnt(eksenNokta.x, eksenNokta.y, eksenNokta.z), gp_Dir(eksenYon.x, eksenYon.y, eksenYon.z)); const f64 aci = aciDerece * std::numbers::pi / 180.0; BRepPrimAPI_MakeRevol donel(yuz, eksen, aci); // yuz (kapali) → KATI donel.Build(); if (!donel.IsDone()) { gunluk::uyari("Döndürme basarisiz (eksen profili kesiyor olabilir)"); return {}; } return sar(donel.Shape()); } catch (const Standard_Failure& h) { gunluk::hata("Döndürme hatasi: {}", h.what()); return {}; } } } // namespace // ---- Sketch-on-Surface: eğri yüzeye sarma (WRAP) + emboss ------------------- // Sketch 2D profilini hedef yüzün UV parametre uzayına afin eşleyip BREP-exact (pcurve+yüzey) // SARILI YÜZ üretir. Ölçek = 1/|türev| (1mm≈1mm yay). Çember/Yay → U/V eksenli elips (afin // anizotropta çember→elips; major=V ise açı -π/2 kayar). Tessellation YOK. sketchYuzeSar + // yuzeKabart bu helper'ı paylaşır. Başarısız → IsNull yüz. // TEK 2D kontur → hedef yüzeyin UV'sinde BREP-exact kapalı wire (pcurve+yüzey). Çizgi→Geom2d // segment, Çember/Yay→U/V eksenli elips (afin). BuildCurves3d ŞART. Başarısız → IsNull. static TopoDS_Wire sariliWireKur(const Handle(Geom_Surface)& srf, f64 uc, f64 vc, f64 ku, f64 kv, const geometri::ProfilKontur2d& kontur) { using Tip = geometri::ProfilEgri2d::Tip; const auto M = [&](const Vec2d& p) { return gp_Pnt2d(uc + p.x * ku, vc + p.y * kv); }; Handle(ShapeExtend_WireData) wd = new ShapeExtend_WireData(); const auto ekle2d = [&](const Handle(Geom2d_Curve)& c2d) { if (c2d.IsNull()) return; TopoDS_Edge e = BRepBuilderAPI_MakeEdge(c2d, srf).Edge(); if (e.IsNull()) { gunluk::uyari("sariliWireKur: MakeEdge(c2d,srf) null"); return; } BRepLib::BuildCurves3d(e); // pcurve'den 3B eğriyi kur — ŞART (yoksa wire/yüz kurulmaz) wd->Add(e); }; for (const geometri::ProfilEgri2d& e : kontur) { if (e.tip == Tip::Cizgi) { ekle2d(GCE2d_MakeSegment(M(e.p0), M(e.p1)).Value()); } else if (e.tip == Tip::Cember || e.tip == Tip::Yay) { const gp_Pnt2d cUV = M(e.merkez); const f64 a = e.yaricap * ku, b = e.yaricap * kv; // U/V eksenli yarı-eksenler f64 kayma = 0.0; Handle(Geom2d_Curve) kon; if (std::abs(a - b) < 1e-9) kon = new Geom2d_Circle(gp_Ax22d(cUV, gp_Dir2d(1, 0), gp_Dir2d(0, 1)), std::max(a, 1e-9)); else if (a >= b) // major = U → param = sketch açısı kon = new Geom2d_Ellipse(gp_Ax22d(cUV, gp_Dir2d(1, 0), gp_Dir2d(0, 1)), a, std::max(b, 1e-9)); else { // major = V → param s = t - π/2 (P(s)=c + b·cos s·V - a·sin s·U) kon = new Geom2d_Ellipse(gp_Ax22d(cUV, gp_Dir2d(0, 1), gp_Dir2d(-1, 0)), b, std::max(a, 1e-9)); kayma = -std::numbers::pi / 2.0; } if (e.tip == Tip::Cember) ekle2d(kon); // kapalı tam elips/çember else { f64 s0 = e.aci0 + kayma, s1 = e.aci1 + kayma; if (s1 <= s0) s1 += 2.0 * std::numbers::pi; ekle2d(new Geom2d_TrimmedCurve(kon, s0, s1)); } } else { gunluk::uyari("sariliWireKur: dönük eliptik profil desteklenmiyor (atlandı)"); } } if (wd->NbEdges() == 0) { gunluk::uyari("sariliWireKur: hiç edge kurulamadı"); return {}; } ShapeFix_Wire fix; fix.Load(wd); fix.SetSurface(srf); // pcurve'lü wire → yüzey bağlamında birleştir/kapat (silindir seam) fix.SetPrecision(1e-3); fix.SetMaxTolerance(1e-2); fix.FixReorder(); fix.FixConnected(); fix.FixClosed(); return fix.Wire(); } // Sarılı YÜZ: konturlar[0]=dış sınır, 1..=DELİKLER. Tessellation YOK. yuzeyOfset≠0 → normal // boyunca kaydırılmış yüzeyde kur. sketchYuzeSar + yuzeKabart paylaşır. Başarısız → IsNull. static TopoDS_Face sariliYuzKur(const TopoDS_Face& hedef, const std::vector& konturlar, f64 yuzeyOfset = 0.0) { if (konturlar.empty() || konturlar[0].empty()) return {}; Handle(Geom_Surface) srf = BRep_Tool::Surface(hedef); if (srf.IsNull()) { gunluk::uyari("sariliYuzKur: yüzey alınamadı"); return {}; } if (std::abs(yuzeyOfset) > 1e-12) srf = new Geom_OffsetSurface(srf, yuzeyOfset); Standard_Real u0, u1, v0, v1; BRepTools::UVBounds(hedef, u0, u1, v0, v1); const f64 uc = 0.5 * (u0 + u1), vc = 0.5 * (v0 + v1); // çapa = yüz UV ortası gp_Pnt P; gp_Vec dU, dV; srf->D1(uc, vc, P, dU, dV); const f64 magU = dU.Magnitude(), magV = dV.Magnitude(); if (magU < 1e-9 || magV < 1e-9) { gunluk::uyari("sariliYuzKur: yüzey türevi dejenere"); return {}; } const f64 ku = 1.0 / magU, kv = 1.0 / magV; // sketch mm → UV param (arc-length koru) const TopoDS_Wire dis = sariliWireKur(srf, uc, vc, ku, kv, konturlar[0]); if (dis.IsNull()) { gunluk::uyari("sariliYuzKur: dış wire kurulamadı"); return {}; } BRepBuilderAPI_MakeFace mf(srf, dis, Standard_True); // yüzey + dış wire → BREP-exact yüz if (!mf.IsDone()) { gunluk::uyari("sariliYuzKur: MakeFace başarısız"); return {}; } for (std::size_t i = 1; i < konturlar.size(); ++i) { // DELİKLER: iç wire (ters yön) ekle if (konturlar[i].empty()) continue; TopoDS_Wire ic = sariliWireKur(srf, uc, vc, ku, kv, konturlar[i]); if (!ic.IsNull()) { ic.Reverse(); mf.Add(ic); } // ShapeFix_Face orientation düzeltir } TopoDS_Face yuz = mf.Face(); ShapeFix_Face ff(yuz); ff.FixOrientation(); return ff.Face(); } BrepGovde sketchYuzeSar(const BrepGovde& g, u32 yuzOrdinal, const geometri::ProfilKontur2d& kontur) { if (!g.gecerliMi() || yuzOrdinal == 0 || kontur.empty()) return {}; occtSinyalKur(); // OSD::SetSignal — OCCT sessiz çökme koruması ([[fikatia-occt-setsignal]]) try { const TopoDS_Face hedef = ordinalYuz(sekil(g), yuzOrdinal); if (hedef.IsNull()) { gunluk::uyari("sketchYuzeSar: yüz eşleşmedi"); return {}; } const TopoDS_Face yuz = sariliYuzKur(hedef, {kontur}); // tek kontur (delik yok) if (yuz.IsNull()) return {}; return sar(yuz); } catch (const Standard_Failure& h) { gunluk::uyari("sketchYuzeSar istisna: {}", h.what()); return {}; } } bool yuzYuzeyNokta(const BrepGovde& g, u32 yuzOrdinal, Vec3d& nokta, Vec3d& normal) { if (!g.gecerliMi() || yuzOrdinal == 0) return false; occtSinyalKur(); try { const TopoDS_Face yuz = ordinalYuz(sekil(g), yuzOrdinal); if (yuz.IsNull()) return false; Standard_Real u0, u1, v0, v1; BRepTools::UVBounds(yuz, u0, u1, v0, v1); const Standard_Real uc = 0.5 * (u0 + u1), vc = 0.5 * (v0 + v1); BRepAdaptor_Surface sf(yuz); gp_Pnt P; gp_Vec dU, dV; sf.D1(uc, vc, P, dU, dV); gp_Vec n = dU.Crossed(dV); if (n.Magnitude() < 1e-9) return false; n.Normalize(); if (yuz.Orientation() == TopAbs_REVERSED) n.Reverse(); // dış normal nokta = {P.X(), P.Y(), P.Z()}; normal = {n.X(), n.Y(), n.Z()}; return true; } catch (const Standard_Failure&) { return false; } } // 2D sketch konturu → dünya-uzayı gerçek-geometri ProfilKontur (düz yüzde Pad/Cep tabanı). ProfilKontur profil2dDunyaya(const ProfilKontur2d& kontur2d, Vec3d orijin, Vec3d u, Vec3d v) { using T2 = ProfilEgri2d::Tip; const Vec3d normal = u.capraz(v).birim(); const auto W = [&](const Vec2d& p) { return orijin + u * p.x + v * p.y; }; // düzlem→dünya ProfilKontur out; out.reserve(kontur2d.size()); for (const ProfilEgri2d& e2 : kontur2d) { ProfilEgri e; e.merkez = W(e2.merkez); e.eksen = normal; e.refX = u; // sketch +x ekseni (rot elipste eklenir) e.yaricap = e2.yaricap; e.kucukYaricap = e2.kucukYaricap; e.aci0 = e2.aci0; e.aci1 = e2.aci1; e.p0 = W(e2.p0); e.p1 = W(e2.p1); switch (e2.tip) { case T2::Cizgi: e.tip = ProfilEgri::Tip::Cizgi; break; case T2::Yay: e.tip = ProfilEgri::Tip::Yay; break; case T2::Cember: e.tip = ProfilEgri::Tip::Cember; break; case T2::Elips: e.tip = ProfilEgri::Tip::Elips; e.refX = (u * std::cos(e2.rot) + v * std::sin(e2.rot)); // elips büyük eksen düzlemde döner break; } out.push_back(e); } return out; } // Sarılı profili (dış + DELİKLER: konturlar[0]=dış, 1..=delik) hedef eğri yüze sarıp normali // boyunca prizma ederek KABARTIR (fuseMi=true, malzeme ekle/boss) / OYAR (false, çıkar/pocket). // NOT: BRepFeat_MakeDPrism eğri profilde ÇALIŞMAZ (BRepFill_Evolved planar ister) → sarılı yüzü // yüzey normali boyunca KALINLAŞTIR (eğriyi izler) → Boolean (fuse glue / cut). Kırılırsa geçersiz. BrepGovde yuzeKabart(const BrepGovde& g, u32 hedefYuzOrdinal, const std::vector& konturlar, f64 mesafe, bool fuseMi) { if (!g.gecerliMi() || hedefYuzOrdinal == 0 || konturlar.empty() || konturlar[0].empty() || std::abs(mesafe) < 1e-6) return {}; occtSinyalKur(); try { const TopoDS_Shape s = tekSolidCoz(sekil(g)); // compound sarili boolean sonucu (PartDesign) desteklenir const TopoDS_Face hedef = ordinalYuz(s, hedefYuzOrdinal); if (hedef.IsNull()) { gunluk::uyari("yuzeKabart: hedef yüz eşleşmedi"); return {}; } const f64 m = std::abs(mesafe); TopoDS_Face profil = sariliYuzKur(hedef, konturlar); // dış + delikler if (profil.IsNull()) { gunluk::uyari("yuzeKabart: sarılı profil yüzü kurulamadı"); return {}; } // Profil yüz normalini hedef yüzün DIŞ normaliyle hizala: aksi halde MakeThickSolidBySimple // ters yöne kalınlaşır → araç yanlış tarafta → Boolean ETKİSİZ (özellikle DÜZ yüzde hacim // değişmiyordu). Aynı yüzey → UV-orta normal karşılaştır (orientation dahil). { BRepAdaptor_Surface hs(hedef), ps(profil); BRepLProp_SLProps hp(hs, 0.5 * (hs.FirstUParameter() + hs.LastUParameter()), 0.5 * (hs.FirstVParameter() + hs.LastVParameter()), 1, 1e-7); BRepLProp_SLProps pp2(ps, 0.5 * (ps.FirstUParameter() + ps.LastUParameter()), 0.5 * (ps.FirstVParameter() + ps.LastVParameter()), 1, 1e-7); if (hp.IsNormalDefined() && pp2.IsNormalDefined()) { gp_Dir nH = hp.Normal(); if (hedef.Orientation() == TopAbs_REVERSED) nH.Reverse(); gp_Dir nP = pp2.Normal(); if (profil.Orientation() == TopAbs_REVERSED) nP.Reverse(); if (nP.Dot(nH) < 0.0) profil.Reverse(); // profil DIŞA baksın (+offset=boss dışa) } } const f64 ofs = fuseMi ? m : -m; // fuse=dışa (boss), cut=içe (pocket) BRepOffsetAPI_MakeThickSolid mts; mts.MakeThickSolidBySimple(profil, ofs); mts.Build(); if (!mts.IsDone()) { gunluk::uyari("yuzeKabart: kalınlaştırma başarısız"); return {}; } TopoDS_Shape arac = mts.Shape(); // MakeThickSolidBySimple +offset (dışa/boss) TERS-oriented (negatif hacim) solid üretir → // Boolean bozulur (fuse yalnız aracı döndürür). Hacim işareti negatifse aracı ters çevir. { GProp_GProps gv; BRepGProp::VolumeProperties(arac, gv); if (gv.Mass() < 0.0) arac.Reverse(); } TopoDS_Shape sonucShape; if (fuseMi) { // Aracın iç yüzü hedef yüzeyle TAM ÇAKIŞIK (boss taban) → BOPAlgo GLUE ile birleştir // (koincident yüzleri tangent-fuse yerine doğrudan glue → tek manifold). Fuzzy destek. BRepAlgoAPI_Fuse op(s, arac); op.SetGlue(BOPAlgo_GlueShift); op.SetFuzzyValue(std::max(m * 1e-3, 1e-5)); op.Build(); if (!op.IsDone()) { gunluk::uyari("yuzeKabart: fuse başarısız"); return {}; } sonucShape = op.Shape(); } else { BRepAlgoAPI_Cut op(s, arac); op.Build(); if (!op.IsDone()) { gunluk::uyari("yuzeKabart: cut başarısız"); return {}; } sonucShape = op.Shape(); } BrepGovde sonuc = sar(sonucShape); if (!sonuc.doluMu()) { gunluk::uyari("yuzeKabart: geçersiz sonuç"); return {}; } return sonuc; } catch (const Standard_Failure& h) { gunluk::uyari("yuzeKabart istisna: {}", h.what()); return {}; } } // Yüz Kaldır (CATIA Remove Face / Delete Face): seçili yüz(ler)i katıdan SİL, komşu yüzleri // uzatarak boşluğu kapat (BRepAlgoAPI_Defeaturing). Fillet/pah/boss/delik gibi feature'ları // geri alma temizler. Komşular kapanamıyorsa (ör. temel kutu yüzü) → geçersiz gövde. BrepGovde yuzKaldir(const BrepGovde& g, const std::vector& yuzler, std::vector* yuzRemap) { if (yuzRemap) yuzRemap->clear(); if (!g.gecerliMi() || yuzler.empty()) return {}; occtSinyalKur(); try { const TopoDS_Shape s = tekSolidCoz(sekil(g)); // compound sarili boolean sonucu (PartDesign) desteklenir TopTools_ListOfShape kaldir; for (u32 ord : yuzler) { const TopoDS_Face f = ordinalYuz(s, ord); if (!f.IsNull()) kaldir.Append(f); } if (kaldir.IsEmpty()) { gunluk::uyari("Yüz kaldır: yüz eşleşmedi"); return {}; } BRepAlgoAPI_Defeaturing df; df.SetShape(s); df.AddFacesToRemove(kaldir); df.Build(); if (!df.IsDone()) { gunluk::uyari("Yüz kaldır: Defeaturing başarısız (komşular kapanamadı?)"); return {}; } BrepGovde sonuc = sar(df.Shape()); if (!sonuc.doluMu()) { gunluk::uyari("Yüz kaldır: geçersiz sonuç"); return {}; } ic::renkPropagasyonu(g, df, sonuc); // uzatılan komşu yüzler eski rengini korur (Defeaturing=MakeShape) if (yuzRemap) { // parametrik feature provenance: eski→yeni yüz ordinal haritası (df=MakeShape) std::vector remap; yuzGeriHaritaKur(s, sekil(sonuc), df, remap); *yuzRemap = std::move(remap); } return sonuc; } catch (const Standard_Failure& h) { gunluk::uyari("Yüz kaldır istisna: {}", h.what()); return {}; } } BrepGovde yuzKopyala(const BrepGovde& g, const std::vector& yuzler) { if (!g.gecerliMi() || yuzler.empty()) return {}; occtSinyalKur(); try { const TopoDS_Shape s = sekil(g); const std::vector yuzList = ordinalYuzler(s, yuzler); if (yuzList.empty()) { gunluk::uyari("Yüz kopyala: yüz eşleşmedi"); return {}; } // Yüzleri açık yüzey gövdesine (shell) topla. GSD Yuzey tipi (hacim yok → gecerliMi tek ölçüt). BRep_Builder b; TopoDS_Shell shell; b.MakeShell(shell); for (const TopoDS_Face& f : yuzList) b.Add(shell, f); // Bağımsız kopya: kaynak katıyla TShape paylaşmasın (ayrı düzenlenebilsin). const TopoDS_Shape bagimsiz = BRepBuilderAPI_Copy(shell).Shape(); BrepGovde sonuc = sar(bagimsiz.IsNull() ? shell : bagimsiz); if (!sonuc.gecerliMi()) { gunluk::uyari("Yüz kopyala: geçersiz shell"); return {}; } return sonuc; } catch (const Standard_Failure& h) { gunluk::uyari("Yüz kopyala istisna: {}", h.what()); return {}; } } BrepGovde yuzeyKatilastir(const BrepGovde& yuzey, f64 kalinlik) { if (!yuzey.gecerliMi() || std::abs(kalinlik) < 1e-6) return {}; occtSinyalKur(); try { const TopoDS_Shape s = sekil(yuzey); BRepOffsetAPI_MakeThickSolid mts; mts.MakeThickSolidBySimple(s, kalinlik); // shell'i normal boyunca kalınlaştır → katı mts.Build(); if (!mts.IsDone()) { gunluk::uyari("Yüzey kalınlaştır: başarısız (açık/dejenere yüzey?)"); return {}; } TopoDS_Shape kati = mts.Shape(); // +offset TERS-oriented (negatif hacim) solid üretebilir → hacim işareti negatifse çevir. { GProp_GProps gv; BRepGProp::VolumeProperties(kati, gv); if (gv.Mass() < 0.0) kati.Reverse(); } BrepGovde sonuc = sar(kati); if (!sonuc.doluMu()) { gunluk::uyari("Yüzey kalınlaştır: geçersiz katı"); return {}; } return sonuc; } catch (const Standard_Failure& h) { gunluk::uyari("Yüzey kalınlaştır istisna: {}", h.what()); return {}; } } BrepGovde yuzeyOfset(const BrepGovde& yuzey, f64 mesafe) { if (!yuzey.gecerliMi() || std::abs(mesafe) < 1e-6) return {}; occtSinyalKur(); try { BRepOffsetAPI_MakeOffsetShape mos; mos.PerformBySimple(sekil(yuzey), mesafe); // yüzeyi normal boyunca basit offset if (!mos.IsDone()) { gunluk::uyari("Yüzey offset: başarısız (dejenere yüzey?)"); return {}; } BrepGovde sonuc = sar(mos.Shape()); if (!sonuc.gecerliMi()) { gunluk::uyari("Yüzey offset: geçersiz sonuç"); return {}; } return sonuc; } catch (const Standard_Failure& h) { gunluk::uyari("Yüzey offset istisna: {}", h.what()); return {}; } } BrepGovde kabuktanKati(const BrepGovde& shell) { if (!shell.gecerliMi()) return {}; occtSinyalKur(); try { // Yüzleri dik (koincident kenar toleransı) → tek kapalı shell. BRepBuilderAPI_Sewing sew(1e-4); sew.Add(sekil(shell)); sew.Perform(); const TopoDS_Shape sewn = sew.SewedShape(); TopoDS_Shell sh; if (sewn.ShapeType() == TopAbs_SHELL) { sh = TopoDS::Shell(sewn); } else { for (TopExp_Explorer ex(sewn, TopAbs_SHELL); ex.More(); ex.Next()) { sh = TopoDS::Shell(ex.Current()); break; } } if (sh.IsNull()) { gunluk::uyari("Kabuktan katı: shell yok (açık yüzey?)"); return {}; } BRepBuilderAPI_MakeSolid ms(sh); if (!ms.IsDone()) { gunluk::uyari("Kabuktan katı: MakeSolid başarısız"); return {}; } TopoDS_Solid kati = ms.Solid(); { GProp_GProps gv; BRepGProp::VolumeProperties(kati, gv); if (gv.Mass() < 0.0) kati.Reverse(); } BrepGovde sonuc = sar(kati); // Açık shell → hacimsiz/geçersiz katı → reddet (yalnız gerçekten kapalı shell geçer). if (!sonuc.doluMu() || !BRepCheck_Analyzer(sekil(sonuc)).IsValid()) { gunluk::uyari("Kabuktan katı: geçersiz (kapalı shell değil?)"); return {}; } return sonuc; } catch (const Standard_Failure& h) { gunluk::uyari("Kabuktan katı istisna: {}", h.what()); return {}; } } std::vector yuzeyIleBol(const BrepGovde& kati, const BrepGovde& yuzey) { std::vector parcalar; if (!kati.gecerliMi() || !yuzey.gecerliMi()) return parcalar; occtSinyalKur(); try { BRepAlgoAPI_Splitter sp; TopTools_ListOfShape args, arac; args.Append(sekil(kati)); arac.Append(sekil(yuzey)); sp.SetArguments(args); sp.SetTools(arac); sp.Build(); if (!sp.IsDone()) { gunluk::uyari("Yüzey ile böl: Splitter başarısız"); return parcalar; } // Sonuç compound → geçerli KATI parçalarını patlat (hacimsiz kırpıntı atlanır). for (TopExp_Explorer ex(sp.Shape(), TopAbs_SOLID); ex.More(); ex.Next()) { BrepGovde p = sar(ex.Current()); if (p.gecerliMi() && p.hacim() > 1e-9) parcalar.push_back(std::move(p)); } } catch (const Standard_Failure& h) { gunluk::uyari("Yüzey ile böl istisna: {}", h.what()); parcalar.clear(); } return parcalar; } BrepGovde dondurKati(const std::vector>& konturlar, Vec3d eksenNokta, Vec3d eksenYon, f64 aciDerece) { if (konturlar.empty()) return {}; const TopoDS_Face yuz = konturYuz(konturlar); if (yuz.IsNull()) { gunluk::uyari("Döndürme: yuz kurulamadi"); return {}; } return revolYuzden(yuz, eksenNokta, eksenYon, aciDerece); } BrepGovde dondurKatiEgri(const std::vector& konturlar, Vec3d eksenNokta, Vec3d eksenYon, f64 aciDerece) { if (konturlar.empty()) return {}; const TopoDS_Face yuz = egriYuzDelikli(konturlar); if (yuz.IsNull()) { gunluk::uyari("DöndürmeEgri: yuz kurulamadi"); return {}; } return revolYuzden(yuz, eksenNokta, eksenYon, aciDerece); } BrepGovde prizmaMultiEgri(const std::vector& konturlar, Vec3d yon, f64 ileri, f64 geri, f64 draftAci) { if (konturlar.empty()) return {}; // Tek kontur → prizmaCiftEgri ile birebir (even-odd gereksiz). if (konturlar.size() == 1) return prizmaCiftEgri(konturlar, yon, ileri, geri, draftAci); // even-odd analizi için her konturu düzlem-içi noktalara örnekle. std::vector> ornek; ornek.reserve(konturlar.size()); for (const ProfilKontur& k : konturlar) ornek.push_back(egriOrnekle(k)); const auto domainler = domainGrupla(ornek, yon); if (domainler.empty()) return {}; return fuseParcalar(domainler.size(), [&](size_t d) { std::vector parcaKontur; parcaKontur.reserve(domainler[d].size()); for (int idx : domainler[d]) parcaKontur.push_back(konturlar[idx]); return prizmaCiftEgri(parcaKontur, yon, ileri, geri, draftAci); }, "prizmaMultiEgri"); } BrepGovde supurmeKatiEgri(const std::vector& konturlar, const std::vector& kilavuzYol) { if (konturlar.empty() || kilavuzYol.size() < 2) return {}; try { // Profil YÜZ (gerçek-geometri; delikli desteklenir) — süpürülen kesit. const TopoDS_Face yuz = egriYuzDelikli(konturlar); if (yuz.IsNull()) { gunluk::uyari("Süpürme: profil yüzü kurulamadı"); return {}; } // Kılavuz yol → spine wire (MVP: polyline; ardışık çok-yakın nokta atlanır → dejenere edge // yok). TODO: gerçek yay/çizgi edge spine + MakePipeShell Frenet (kavisli kılavuzda facetsiz). BRepBuilderAPI_MakePolygon poli; gp_Pnt onceki; bool ilk = true; int n = 0; for (const Vec3d& p : kilavuzYol) { const gp_Pnt gp(p.x, p.y, p.z); if (!ilk && gp.Distance(onceki) < 1e-7) continue; poli.Add(gp); onceki = gp; ilk = false; ++n; } if (n < 2 || !poli.IsDone()) { gunluk::uyari("Süpürme: kılavuz spine wire kurulamadı"); return {}; } const TopoDS_Wire spine = poli.Wire(); BRepOffsetAPI_MakePipe boru(spine, yuz); // YÜZ profil → KATI sonuç boru.Build(); if (!boru.IsDone()) { gunluk::uyari("Süpürme başarısız (kılavuz profili kesiyor olabilir)"); return {}; } return sar(boru.Shape()); } catch (const Standard_Failure& h) { gunluk::hata("Süpürme hatası: {}", h.what()); return {}; } } BrepGovde loftKati(const std::vector& kesitler, bool kapali) { if (kesitler.size() < 2) return {}; try { // isSolid=kapali (kapalı katı, uçları planar yüzle örter), ruled=false → smooth (yumuşak // geçiş; CATIA Loft varsayılanı). Her kesit gerçek-geometri edge'li tel — facet yok. BRepOffsetAPI_ThruSections uretici(/*isSolid*/ kapali, /*ruled*/ Standard_False, /*pres*/ 1e-6); int eklenen = 0; for (const ProfilKontur& k : kesitler) { const TopoDS_Wire w = egriKonturWire(k); // kontur ilk noktasından başlar (kapanış uyumu) if (w.IsNull()) { gunluk::uyari("Loft: kesit teli kurulamadı (atlandı)"); continue; } uretici.AddWire(w); ++eklenen; } if (eklenen < 2) { gunluk::uyari("Loft: en az 2 geçerli kesit gerekli ({} eklendi)", eklenen); return {}; } uretici.Build(); if (!uretici.IsDone()) { gunluk::uyari("Loft başarısız (kesitler uyumsuz/kesişiyor olabilir)"); return {}; } return sar(uretici.Shape()); } catch (const Standard_Failure& h) { gunluk::hata("Loft hatası: {}", h.what()); return {}; } } namespace { // merkez + birim normal (delik ekseni, govdeye GIREN yon) → gp_Ax2 (silindir/koni // yerel eksen; Z = ekseni). Silindir/koni OCCT'de +Z boyunca buyur. gp_Ax2 delikEkseni(const Vec3d& merkez, const Vec3d& normal) { return gp_Ax2(gp_Pnt(merkez.x, merkez.y, merkez.z), gp_Dir(normal.x, normal.y, normal.z)); } } // namespace BrepGovde delikAraci(Vec3d merkez, Vec3d normal, const HoleParam& hp) { const f64 nlen = std::sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z); if (hp.cap <= 1e-6 || hp.derinlik <= 1e-6 || nlen < 1e-12) return {}; normal = {normal.x / nlen, normal.y / nlen, normal.z / nlen}; const f64 r = hp.cap * 0.5; // eksen boyunca t mesafedeki dunya noktasi (agizdan iceri) → alt-koni/silindir konumu. const auto ekseneOfset = [&](f64 t) { return Vec3d{merkez.x + normal.x * t, merkez.y + normal.y * t, merkez.z + normal.z * t}; }; const f64 kPi = std::numbers::pi; try { const gp_Ax2 eks = delikEkseni(merkez, normal); // ---- Ana gövde: Basit=silindir, Tapered=konik ---- f64 dipR = r; // ana gövde tabanindaki yaricap (V-dip / gecis konisi taban capi icin) TopoDS_Shape araci; if (hp.tip == DelikTip::Tapered && hp.taperAci > 1e-6) { // Konik delik: agizda r, dipe dogru daralir (yan cidar acisi = taperAci, eksenden). const f64 tanA = std::tan(hp.taperAci * kPi / 180.0); dipR = std::max(r * 0.05, r - hp.derinlik * tanA); // dejenere olmayacak sekilde kirp araci = BRepPrimAPI_MakeCone(eks, r, dipR, hp.derinlik).Shape(); // agiz r → dip dipR } else { araci = BRepPrimAPI_MakeCylinder(eks, r, hp.derinlik).Shape(); } // ---- Matkap ucu (V-Bottom): dibe vDipAci koni ekle (uc govde icine dogru) ---- if (hp.vDip) { const f64 yariAci = hp.vDipAci * 0.5 * kPi / 180.0; const f64 konYuk = dipR / std::tan(yariAci); // koni yuksekligi (uc uzunlugu) if (konYuk > 1e-6) { // Koni dibi ana gövde tabaninda (derinlik), taban dipR → uc 0, +normal yonunde. const gp_Ax2 konEks = delikEkseni(ekseneOfset(hp.derinlik), normal); const TopoDS_Shape koni = BRepPrimAPI_MakeCone(konEks, dipR, 0.0, konYuk).Shape(); araci = BRepAlgoAPI_Fuse(araci, koni).Shape(); } } // ---- Counterbore fatura: agiza genis silindir (Counterbore + Counterdrill) ---- if ((hp.tip == DelikTip::Counterbore || hp.tip == DelikTip::Counterdrill) && hp.cbCap > hp.cap && hp.cbDerinlik > 1e-6) { const TopoDS_Shape cb = BRepPrimAPI_MakeCylinder(eks, hp.cbCap * 0.5, hp.cbDerinlik).Shape(); araci = BRepAlgoAPI_Fuse(araci, cb).Shape(); } // ---- Countersink havsa: agiza koni (ust csCap → alt cap, csAci ile) ---- if (hp.tip == DelikTip::Countersink && hp.csCap > hp.cap) { const f64 ustR = hp.csCap * 0.5; const f64 yariAci = hp.csAci * 0.5 * kPi / 180.0; const f64 csYuk = (ustR - r) / std::tan(yariAci); // havsa derinligi if (csYuk > 1e-6) { const TopoDS_Shape cs = BRepPrimAPI_MakeCone(eks, ustR, r, csYuk).Shape(); // agiz ustR → r araci = BRepAlgoAPI_Fuse(araci, cs).Shape(); } } // ---- Counterdrill: faturanin ALTINA havsa gecis konisi (cbCap → cap) ---- // CATIA Counterdrilled = counterbore + tabaninda konik gecis (ana deliğe iner). if (hp.tip == DelikTip::Counterdrill && hp.csCap > hp.cap) { const f64 ustR = hp.csCap * 0.5; const f64 yariAci = hp.csAci * 0.5 * kPi / 180.0; const f64 csYuk = (ustR - r) / std::tan(yariAci); if (csYuk > 1e-6) { // Gecis konisi fatura dibinden (cbDerinlik) baslar: agizda ustR → csYuk sonra r. const gp_Ax2 csEks = delikEkseni(ekseneOfset(hp.cbDerinlik), normal); const TopoDS_Shape cs = BRepPrimAPI_MakeCone(csEks, ustR, r, csYuk).Shape(); araci = BRepAlgoAPI_Fuse(araci, cs).Shape(); } } return sar(araci); } catch (const Standard_Failure& h) { gunluk::hata("Delik araci hatasi: {}", h.what()); return {}; } } // ---- Analiz WB (Dalga A): Kesit egrisi + Et kalinligi --------------------- std::vector> brepKesit(const BrepGovde& g, Vec3d duzlemNokta, Vec3d duzlemNormal, f64 sapma) { std::vector> r; if (!g.gecerliMi()) return r; const f64 nLen = std::sqrt(duzlemNormal.x * duzlemNormal.x + duzlemNormal.y * duzlemNormal.y + duzlemNormal.z * duzlemNormal.z); if (nLen < 1e-9) return r; try { const gp_Pln duzlem(gp_Pnt(duzlemNokta.x, duzlemNokta.y, duzlemNokta.z), gp_Dir(duzlemNormal.x, duzlemNormal.y, duzlemNormal.z)); // BRepAlgoAPI_Section: gercek B-Rep kesit → kenar (TopoDS_Edge) kumesi. BRepAlgoAPI_Section kesit(sekil(g), duzlem, false); kesit.ComputePCurveOn1(true); kesit.Approximation(true); kesit.Build(); if (!kesit.IsDone()) return r; const TopoDS_Shape kenarSekli = kesit.Shape(); for (TopExp_Explorer k(kenarSekli, TopAbs_EDGE); k.More(); k.Next()) { const TopoDS_Edge kenar = TopoDS::Edge(k.Current()); BRepAdaptor_Curve egri(kenar); GCPnts_QuasiUniformDeflection ayrik(egri, sapma); if (!ayrik.IsDone() || ayrik.NbPoints() < 2) continue; std::vector polyline; const int n = ayrik.NbPoints(); polyline.reserve(n); for (int i = 1; i <= n; ++i) { const gp_Pnt p = ayrik.Value(i); polyline.push_back({p.X(), p.Y(), p.Z()}); } r.push_back(std::move(polyline)); } } catch (const Standard_Failure& hata) { gunluk::hata("Kesit hatasi: {}", hata.what()); } return r; } std::vector brepKesitKapak(const BrepGovde& g, Vec3d duzlemNokta, Vec3d duzlemNormal, f64 kaliteOrani) { std::vector r; if (!g.gecerliMi()) return r; const f64 nLen = std::sqrt(duzlemNormal.x * duzlemNormal.x + duzlemNormal.y * duzlemNormal.y + duzlemNormal.z * duzlemNormal.z); if (nLen < 1e-9) return r; try { // Govde sinir kutusu → duzlem yuzunu govdeyi asacak kadar buyuk kur. Bnd_Box kutucuk; BRepBndLib::Add(sekil(g), kutucuk); if (kutucuk.IsVoid()) return r; const f64 kosegen = std::sqrt(kutucuk.SquareExtent()); const f64 R = std::max(kosegen, 1.0) * 1.2; const gp_Pln duzlem(gp_Pnt(duzlemNokta.x, duzlemNokta.y, duzlemNokta.z), gp_Dir(duzlemNormal.x / nLen, duzlemNormal.y / nLen, duzlemNormal.z / nLen)); const TopoDS_Face yuz = BRepBuilderAPI_MakeFace(duzlem, -R, R, -R, R).Face(); // Kati ∩ duzlem-yuzu → duzlemin govde icinde kalan planar bolgesi (kapak). BRepAlgoAPI_Common ortak(sekil(g), yuz); ortak.Build(); if (!ortak.IsDone()) return r; TopoDS_Shape kapak = ortak.Shape(); if (kapak.IsNull()) return r; // Kapak yuzlerini ucgenle ve topla. isInParallel=false: OSD_ThreadPool KULLANMA — // nested/eşzamanlı OCCT paralel işlemle "resource deadlock" önle (brep.cpp:332 kalıbı). const f64 sapma = std::max(kosegen * kaliteOrani, 1e-4); BRepMesh_IncrementalMesh(kapak, sapma, false, 0.35, false); for (TopExp_Explorer kesif(kapak, TopAbs_FACE); kesif.More(); kesif.Next()) { const TopoDS_Face f = TopoDS::Face(kesif.Current()); TopLoc_Location konum; const Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(f, konum); if (tri.IsNull()) continue; const gp_Trsf t = konum.Transformation(); const int nUcgen = tri->NbTriangles(); r.reserve(r.size() + (size_t)nUcgen * 3); for (int i = 1; i <= nUcgen; ++i) { int a, b, c; tri->Triangle(i).Get(a, b, c); for (int k : {a, b, c}) { const gp_Pnt p = tri->Node(k).Transformed(t); r.push_back({p.X(), p.Y(), p.Z()}); } } } } catch (const Standard_Failure& hata) { gunluk::hata("Kesit kapak hatasi: {}", hata.what()); r.clear(); } return r; } namespace { // Yuklu (Load edilmis) kesisim nesnesiyle tek isin: Init(shape,...) HER isinde // tum govdeyi yeniden yukluyordu (yuz listesi/kutular) — 64-ornekli yuzde 64x // gereksiz kurulum. Load bir kez + Init(line) cok kez OCCT'nin amaclanan kullanimi // (olculdu: kalinlikRapor 66 yuz/4224 isin 1309.9 ms → kokun buyugu bu kurulum). // // OLCEK-BAGIMSIZ EPS: ofset model olcegine gore buyumez (kendi yuzune sifir // mesafeden carpmayi onler) ama asla cidar olceginden buyuk olmaz — aksi halde // ince cidarli buyuk modellerde (200mm govde, 0.05mm cidar) eps cidadi yutar ve // "karsi duvar yok" (-1) donerdi. undercutRapor'daki kosegen*1e-5 kalibi burada // YETMEZ: 200mm govdede eps=0.34mm > 0.05mm cidar. Bu yuzden cosegen*1e-7 taban // alindi ve 1e-4 ile sinirlendi (bu deger OCC toleransindan ve makul mm tabaninda // kucuk bir ofset; 0.1mm altindaki cidarlar zaten BREP toleransiyla temsil edilemez). f64 noktaKalinlikYuklu(BRepIntCurveSurface_Inter& kesisim, Vec3d nokta, Vec3d normal, f64 enBuyuk, f64 epsSabit = 1e-4) { const f64 nLen = std::sqrt(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z); if (nLen < 1e-9) return -1.0; const Vec3d icYon{-normal.x / nLen, -normal.y / nLen, -normal.z / nLen}; try { // Baslangici cok minik ic ofsetle → kendi yuzunu (t~0) sayma. const f64 eps = epsSabit; const gp_Pnt bas(nokta.x + icYon.x * eps, nokta.y + icYon.y * eps, nokta.z + icYon.z * eps); const gp_Lin isin(bas, gp_Dir(icYon.x, icYon.y, icYon.z)); // Yuklu kullanimda Init yalniz GeomAdaptor_Curve alir (gp_Lin overload'i // shape'li tam Init'te) → cizgiyi adaptore sar. kesisim.Init(GeomAdaptor_Curve(new Geom_Line(isin))); f64 enYakin = -1.0; for (; kesisim.More(); kesisim.Next()) { const f64 t = kesisim.W(); // isin parametresi (mesafe, cunku yon birim) if (t <= eps + 1e-6 || t > enBuyuk) continue; if (enYakin < 0.0 || t < enYakin) enYakin = t; } return enYakin < 0.0 ? -1.0 : enYakin + eps; // ic ofseti geri ekle } catch (const Standard_Failure&) { return -1.0; } } } // namespace f64 noktaKalinlik(const BrepGovde& g, Vec3d nokta, Vec3d normal, f64 enBuyuk) { if (!g.gecerliMi()) return -1.0; try { BRepIntCurveSurface_Inter kesisim; kesisim.Load(sekil(g), 1e-6); return noktaKalinlikYuklu(kesisim, nokta, normal, enBuyuk); } catch (const Standard_Failure&) { return -1.0; } } // ---- Undercut: EXACT per-yuz gorunurluk (bkz. brep.hpp sozlesme) ---------- std::vector undercutRapor(const BrepGovde& g, const Donusumd& donusum, Vec3d cekimYonu, f64 ornekAralik, u32 maksIzgara) { std::vector r; if (!g.gecerliMi()) return r; const f64 yl = std::sqrt(cekimYonu.x * cekimYonu.x + cekimYonu.y * cekimYonu.y + cekimYonu.z * cekimYonu.z); if (yl < 1e-12) return r; occtSinyalKur(); // OSD::SetSignal — OCCT sessiz cokme korumasi // Cekim yonu DUNYA'da gelir; BREP YEREL uzayda → yonelim eslenigi ile yerele tasi. // (Olcek yonu bozmaz varsayimi — draftRapor'daki dunyaNormal ile ayni kabul.) const Vec3d yd{cekimYonu.x / yl, cekimYonu.y / yl, cekimYonu.z / yl}; const Vec3d yy = donusum.yonelim.eslenik().dondur(yd); const f64 yyl = std::sqrt(yy.x * yy.x + yy.y * yy.y + yy.z * yy.z); if (yyl < 1e-12) return r; const gp_Dir yon(yy.x / yyl, yy.y / yyl, yy.z / yyl); const u32 maksN = std::clamp(maksIzgara, 2u, 256u); try { const TopoDS_Shape& sek = sekil(g); // Sinir kutusundan olcek-bagimsiz eps (kendi yuzune sifir-mesafeden carpmayi onler). Bnd_Box kutu; BRepBndLib::Add(sek, kutu); f64 x0, y0, z0, x1, y1, z1; if (kutu.IsVoid()) return r; kutu.Get(x0, y0, z0, x1, y1, z1); const f64 kosegen = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0) + (z1 - z0) * (z1 - z0)); const f64 eps = std::max(kosegen * 1e-5, 1e-7); // Ornek adimi (mm). Otomatik: gövde kösegeni/150 → 500mm parcada ~3.3mm → // bu inceliktekii kaburga golgesi artik kacmaz. const f64 adim = ornekAralik > 0.0 ? ornekAralik : std::max(kosegen / 150.0, 1e-6); // Govde yuku BIR kez (Init(shape,…) her isinde tum govdeyi yeniden yukluyordu — // kalinlik ile ayni patoloji; olculdu: tarakta 2054 ms'nin buyugu bu kurulum). BRepIntCurveSurface_Inter kesisim; kesisim.Load(sek, 1e-6); u32 ord = 0; // TopExp_Explorer SIRASI = ordinal (brep.cpp ordinalCache ile ayni kabul — // ordinal SIRAdir, IndexedMap DEGIL: [[fikatia-brep-topology-ilke]]). for (TopExp_Explorer k(sek, TopAbs_FACE); k.More(); k.Next()) { ++ord; const TopoDS_Face yuz = TopoDS::Face(k.Current()); UndercutYuz uy; uy.ordinal = ord; // Duz yuzun normali yuzBilgi'den (draftRapor ile AYNI kaynak → mesh/shader ile // tutarli). yuzNoktaNormali gercek STEP'te bazi duz yuzlerde ZIT isaret verdi. YuzBilgi yb; const bool ybVar = g.yuzBilgi(ord, yb, false); Standard_Real u0, u1, v0, v1; BRepTools::UVBounds(yuz, u0, u1, v0, v1); if (!(u1 > u0) || !(v1 > v0)) { r.push_back(uy); continue; } BRepAdaptor_Surface sf(yuz); BRepClass_FaceClassifier sinif; const f64 tol = std::max(BRep_Tool::Tolerance(yuz), 1e-7); // UYARLANAN IZGARA: yuzun 3B boyutunu UV turevinden olc (duzlem/silindirde // KESIN, egri yuzeyde iyi yaklasim) → adim mm'lik ornek sikligi sec. u32 nu = maksN, nv = maksN; { gp_Pnt P0; gp_Vec dU0, dV0; sf.D1(0.5 * (u0 + u1), 0.5 * (v0 + v1), P0, dU0, dV0); const f64 uzU = dU0.Magnitude() * (u1 - u0); // yuzun u boyunca ~3B uzunlugu const f64 uzV = dV0.Magnitude() * (v1 - v0); if (std::isfinite(uzU) && uzU > 0) nu = (u32)std::clamp(std::ceil(uzU / adim), 2.0, (f64)maksN); if (std::isfinite(uzV) && uzV > 0) nv = (u32)std::clamp(std::ceil(uzV / adim), 2.0, (f64)maksN); } u32 toplam = 0, ortulu = 0; for (u32 iu = 0; iu < nu; ++iu) for (u32 iv = 0; iv < nv; ++iv) { // Hucre MERKEZLERI: kenarda ornekleme (u0/u1) trim sinirinda kararsiz. const f64 u = u0 + (u1 - u0) * ((f64)iu + 0.5) / (f64)nu; const f64 v = v0 + (v1 - v0) * ((f64)iv + 0.5) / (f64)nv; // Trim disi mi? (UV kutusu yuzun gercek sinirindan buyuk olabilir — // delikli/kirpik yuzde sart, yoksa bosluktan isin atariz.) sinif.Perform(yuz, gp_Pnt2d(u, v), tol); if (sinif.State() != TopAbs_IN) continue; gp_Pnt P; gp_Vec dU, dV; sf.D1(u, v, P, dU, dV); // ⚠️ NORMAL: kendi dU×dV + Orientation hesabimi KULLANMA — gercek STEP'te // yuzBilgi ile ZIT isaret verdi (ters yuz "acik" cikti). Kanitlanmis yolu // kullan: yuzNoktaNormali (draftRapor da bunu kullanir; orientation + // location bookkeeping'i ICINDE dogru yapar). Vec3d yerelN{}; if (ybVar && yb.duzlemMi) yerelN = yb.normal; // draftRapor ile ayni kaynak else if (!g.yuzNoktaNormali(ord, Vec3d{P.X(), P.Y(), P.Z()}, yerelN)) continue; const f64 nl = std::sqrt(yerelN.x * yerelN.x + yerelN.y * yerelN.y + yerelN.z * yerelN.z); if (nl < 1e-12) continue; // dejenere (kutup) const gp_Vec nv(yerelN.x / nl, yerelN.y / nl, yerelN.z / nl); ++toplam; // ⭐ IKI KOSUL — bir nokta d yonunden ERISILEBILIR ⟺ // (1) YUZ O YONE BAKAR (n·d >= 0) ve // (2) ONU KAPALI DEGIL (isin bos cikar) // (1)'i atlayip yalniz gorunurluge bakmak YANLIS (yasandi): normali TAM // ASAGI bakan bir yuzun uzeri bos olabilir (disa tasan bir cikintinin // alti) → isin kacar → "acik" sanilir. Oysa +Z'den o yuzun SIRTINA // bakiyorsun; kalip/takim onu o yonden HIC olusturamaz. Draft esigi 0: // dikey duvar (n·d == 0) sinir durum, ERISILEBILIR sayilir. if (nv.Dot(yon) < -1e-9) { ++ortulu; continue; } // (1) ters yuz → erisilemez // Baslangici NORMAL boyunca disari it: cekim yonu boyunca itmek duz // duvarda yuzeyin ICINDE kalabilirdi (yon yuzeye paralel). const gp_Pnt bas(P.X() + nv.X() * eps, P.Y() + nv.Y() * eps, P.Z() + nv.Z() * eps); kesisim.Init(GeomAdaptor_Curve(new Geom_Line(gp_Lin(bas, yon)))); for (; kesisim.More(); kesisim.Next()) if (kesisim.W() > eps) { ++ortulu; break; } // (2) bir engel yeter } if (toplam == 0) { r.push_back(uy); continue; } // Bilinmiyor uy.ortuluOran = (f32)ortulu / (f32)toplam; uy.durum = ortulu == 0 ? UndercutDurum::Acik : ortulu == toplam ? UndercutDurum::Kapali : UndercutDurum::Kismi; r.push_back(uy); } } catch (const Standard_Failure& h) { gunluk::uyari("undercutRapor istisna: {}", h.what()); r.clear(); } return r; } // ---- Kalinlik: EXACT govde-capinda per-yuz duvar kalinligi (bkz. brep.hpp) -- // undercutRapor'un adaptif-izgara/tek-Load iskeleti aynen kullanilir; farkli // olan tek sey her ornekte n·yon karsilastirmasi degil, noktaKalinlikYuklu ile // olculen mesafenin min/ortalamasinin biriktirilmesidir. std::vector kalinlikRapor(const BrepGovde& g, f64 ornekAralik, u32 maksIzgara, f64 enBuyuk) { std::vector r; if (!g.gecerliMi()) return r; occtSinyalKur(); // OSD::SetSignal — OCCT sessiz cokme korumasi const u32 maksN = std::clamp(maksIzgara, 2u, 256u); try { const TopoDS_Shape& sek = sekil(g); Bnd_Box kutu; BRepBndLib::Add(sek, kutu); f64 x0, y0, z0, x1, y1, z1; if (kutu.IsVoid()) return r; kutu.Get(x0, y0, z0, x1, y1, z1); const f64 kosegen = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0) + (z1 - z0) * (z1 - z0)); const f64 adim = ornekAralik > 0.0 ? ornekAralik : std::max(kosegen / 150.0, 1e-6); // Govde yuku BIR kez (yuzKalinlikOrnekle'yi yuz basina cagirmak govde basina // tekrar Load demek olurdu — noktaKalinlikYuklu yorumundaki kurulum maliyeti). BRepIntCurveSurface_Inter kesisim; kesisim.Load(sek, 1e-6); u32 ord = 0; // TopExp_Explorer SIRASI = ordinal (undercutRapor ile ayni kabul). for (TopExp_Explorer k(sek, TopAbs_FACE); k.More(); k.Next()) { ++ord; const TopoDS_Face yuz = TopoDS::Face(k.Current()); KalinlikYuz ky; ky.ordinal = ord; YuzBilgi yb; const bool ybVar = g.yuzBilgi(ord, yb, false); const bool duz = ybVar && yb.duzlemMi && yb.normal.uzunlukKare() >= 1e-12; Standard_Real u0, u1, v0, v1; BRepTools::UVBounds(yuz, u0, u1, v0, v1); if (!(u1 > u0) || !(v1 > v0)) { r.push_back(ky); continue; } BRepAdaptor_Surface sf(yuz); BRepClass_FaceClassifier sinif; const f64 tol = std::max(BRep_Tool::Tolerance(yuz), 1e-7); // UYARLANAN IZGARA: undercutRapor ile ayni yontem (yuzun 3B boyutu UV // turevinden — kucuk yuz ucuz, buyuk yuz yogun). u32 nu = maksN, nv = maksN; { gp_Pnt P0; gp_Vec dU0, dV0; sf.D1(0.5 * (u0 + u1), 0.5 * (v0 + v1), P0, dU0, dV0); const f64 uzU = dU0.Magnitude() * (u1 - u0); const f64 uzV = dV0.Magnitude() * (v1 - v0); if (std::isfinite(uzU) && uzU > 0) nu = (u32)std::clamp(std::ceil(uzU / adim), 2.0, (f64)maksN); if (std::isfinite(uzV) && uzV > 0) nv = (u32)std::clamp(std::ceil(uzV / adim), 2.0, (f64)maksN); } f64 toplamKalinlik = 0.0, minKalinlik = -1.0; u32 ornekSayisi = 0; // 2026-08-09 kararlilik denetimi: ham ornekler tutulur (ortKalinlik hesabi asagida // minKalinlik'e gore filtrelenir - dongu sonrasi aciklamaya bak). std::vector kalinlikOrnekleri; kalinlikOrnekleri.reserve((size_t)nu * nv); for (u32 iu = 0; iu < nu; ++iu) for (u32 iv = 0; iv < nv; ++iv) { // Hucre MERKEZLERI: kenarda ornekleme trim sinirinda kararsiz. const f64 u = u0 + (u1 - u0) * ((f64)iu + 0.5) / (f64)nu; const f64 v = v0 + (v1 - v0) * ((f64)iv + 0.5) / (f64)nv; sinif.Perform(yuz, gp_Pnt2d(u, v), tol); if (sinif.State() != TopAbs_IN) continue; // trim disi const gp_Pnt P = sf.Value(u, v); const Vec3d p{P.X(), P.Y(), P.Z()}; // Duz yuzde yuzBilgi normali (draftRapor/undercutRapor ile ayni kaynak // — gercek STEP'te kendi dU×dV isareti ZIT verebiliyor); egik yuzde // nokta basi yuzNoktaNormali. Vec3d nrm = yb.normal; if (!duz && !g.yuzNoktaNormali(ord, p, nrm)) continue; if (nrm.uzunlukKare() < 1e-12) continue; // dejenere (kutup) const f64 kln = noktaKalinlikYuklu(kesisim, p, nrm, enBuyuk); if (kln <= 0) continue; // karsi duvar yok (acik govde/delik) ++ornekSayisi; toplamKalinlik += kln; kalinlikOrnekleri.push_back(kln); if (minKalinlik < 0.0 || kln < minKalinlik) minKalinlik = kln; } if (ornekSayisi == 0) { r.push_back(ky); continue; } // gecerli=false ky.gecerli = true; ky.ornekSayisi = ornekSayisi; ky.minKalinlik = (f32)minKalinlik; // 2026-08-09 kararlilik denetimi (Fikri: kalinlik_ic_bosluklu_kabuk_iki_mm testi): // GOVDE KOSESINE yakin ornek noktalarinda ic-yonlu isin, KARSISINDAKI (paralel) // duvari degil, govdenin komsu YAN duvarini capraz vurabilir (dogru duvarin o // noktanin tam altinda/ustunde BITMEDIGI durumlarda - orn. bir kabugun disi 20x20, // ic kavitesi 16x16 ise, disin 2mm'lik kenar seridindeki isinlar yan duvara capraz // gider). Bu ornekler GECERLI (karsi duvar var, cokme yok) ama o noktanin YEREL // kalinligini TEMSIL ETMEZ - ortalamayi carpitir (olculmus vaka: min=2mm dogru, // ort=7.5mm - kirilma degeri, gercek et kalinligi degil). Ortalama; minimuma // yakin (<=1.5x) "ayni duvar" kumesinden alinir, uzak-carpik ornekler DISLANIR // (ornekSayisi/minKalinlik degismez - o istatistikler HALA TUM ornekleri yansitir, // yalniz gosterge degeri olan ortKalinlik filtrelenir). f64 filtreliToplam = 0.0; u32 filtreliSay = 0; const f64 esik = minKalinlik * 1.5; for (const f64 v : kalinlikOrnekleri) if (v <= esik) { filtreliToplam += v; ++filtreliSay; } ky.ortKalinlik = (f32)(filtreliSay ? filtreliToplam / filtreliSay : toplamKalinlik / ornekSayisi); r.push_back(ky); } } catch (const Standard_Failure& h) { gunluk::uyari("kalinlikRapor istisna: {}", h.what()); r.clear(); } return r; } bool undercutGolgeMesh(const BrepGovde& g, const Donusumd& donusum, Vec3d cekimYonu, const std::vector& yuzOrdinaller, std::vector& konum, std::vector& normal, std::vector& indeks, f64 kaliteOrani) { konum.clear(); normal.clear(); indeks.clear(); if (!g.gecerliMi() || yuzOrdinaller.empty()) return false; const f64 yl = std::sqrt(cekimYonu.x * cekimYonu.x + cekimYonu.y * cekimYonu.y + cekimYonu.z * cekimYonu.z); if (yl < 1e-12) return false; occtSinyalKur(); // Dunya → yerel yon (BREP yerel uzayda; undercutRapor ile ayni kabul). const Vec3d yd{cekimYonu.x / yl, cekimYonu.y / yl, cekimYonu.z / yl}; const Vec3d yy = donusum.yonelim.eslenik().dondur(yd); const f64 yyl = std::sqrt(yy.x * yy.x + yy.y * yy.y + yy.z * yy.z); if (yyl < 1e-12) return false; const gp_Vec yon(yy.x / yyl, yy.y / yyl, yy.z / yyl); try { const TopoDS_Shape& sek = sekil(g); Bnd_Box kutu; BRepBndLib::Add(sek, kutu); if (kutu.IsVoid()) return false; f64 x0, y0, z0, x1, y1, z1; kutu.Get(x0, y0, z0, x1, y1, z1); const f64 kosegen = std::sqrt((x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0) + (z1 - z0) * (z1 - z0)); if (!(kosegen > 0)) return false; const f64 L = kosegen * 2.0; // supurme uzunlugu (govdeyi kesin asar) const f64 eps = kosegen * 1e-5; // kendini engel saymama ofseti // Ordinal → yuz: TEK TopExp gecisi (ordinalAltSekil O(n) → N yuzde O(N²) olurdu). std::vector hedefler; { std::vector ordSirali = yuzOrdinaller; std::sort(ordSirali.begin(), ordSirali.end()); u32 ord = 0; size_t i = 0; for (TopExp_Explorer k(sek, TopAbs_FACE); k.More() && i < ordSirali.size(); k.Next()) { ++ord; if (ord == ordSirali[i]) { hedefler.push_back(TopoDS::Face(k.Current())); ++i; } } } if (hedefler.empty()) return false; BRep_Builder bldr; TopoDS_Compound golgeBirikim; bldr.MakeCompound(golgeBirikim); bool varMi = false; for (const TopoDS_Face& F : hedefler) { try { // F' = F + yon·eps (kendi yuzunu engel saymasin) gp_Trsf ot; ot.SetTranslation(yon * eps); const TopoDS_Shape Fo = BRepBuilderAPI_Transform(F, ot, Standard_True).Shape(); // G = yuzun USTUNDEKI hacim const TopoDS_Shape G = BRepPrimAPI_MakePrism(Fo, yon * L).Shape(); if (G.IsNull()) continue; // A = govdenin o hacimdeki parcalari = ENGELLER BRepAlgoAPI_Common ortak1(sek, G); if (!ortak1.IsDone()) continue; const TopoDS_Shape A = ortak1.Shape(); if (A.IsNull()) continue; // Bos mu? (hic engel yok → yuz aslinda tam acik) TopExp_Explorer bosK(A, TopAbs_FACE); if (!bosK.More()) continue; // B = engelleri yuze dogru supur (izdusum). // ⚠️ MakePrism SOLID'i supuremez (yalniz boyut<3: yuz→solid). A bir SOLID → // onun YUZLERINI supur. Kapali govdede her dikey dogru en az bir yuzden gecer // → yuzlerin supurumunun BIRLESIMI = A'nin izdusumu (tam ortusme). TopTools_ListOfShape araclar; for (TopExp_Explorer fk(A, TopAbs_FACE); fk.More(); fk.Next()) { const TopoDS_Face af = TopoDS::Face(fk.Current()); // Yone PARALEL yuzu supurmek SIFIR HACIM uretir → BOP'u bozar (yanlis/tasan // sonuc; yasandi). Zaten izdusume katki vermez: kapali govdede footprint, // yone BAKAN yuzlerin supurumunden cikar. Atla. { Standard_Real au0, au1, av0, av1; BRepTools::UVBounds(af, au0, au1, av0, av1); BRepAdaptor_Surface asf(af); gp_Pnt AP; gp_Vec adU, adV; asf.D1(0.5 * (au0 + au1), 0.5 * (av0 + av1), AP, adU, adV); gp_Vec an = adU.Crossed(adV); if (an.Magnitude() < 1e-12) continue; an.Normalize(); if (std::abs(an.Dot(yon)) < 1e-3) continue; // yone paralel → dejenere } const TopoDS_Shape pr = BRepPrimAPI_MakePrism(af, -yon * (L + 2.0 * eps)).Shape(); if (!pr.IsNull()) araclar.Append(pr); } if (araclar.IsEmpty()) continue; // SH = yuzun GOLGELI alt-bolgesi ⭐ (F ∩ birlesim(araclar)) TopTools_ListOfShape girdiler; girdiler.Append(F); BRepAlgoAPI_Common ortak2; ortak2.SetArguments(girdiler); ortak2.SetTools(araclar); ortak2.Build(); if (!ortak2.IsDone()) continue; const TopoDS_Shape SH = ortak2.Shape(); if (SH.IsNull()) continue; TopExp_Explorer shK(SH, TopAbs_FACE); if (!shK.More()) continue; bldr.Add(golgeBirikim, SH); varMi = true; } catch (const Standard_Failure&) { continue; // tek yuz basarisiz → digerleri surur (kismi sonuc > hic sonuc) } } if (!varMi) return false; // Ucgenle. isInParallel=false ZORUNLU (OCCT OSD_ThreadPool deadlock — CLAUDE.md). const f64 sapma = std::max(kosegen * kaliteOrani, 1e-4); BRepMesh_IncrementalMesh(golgeBirikim, sapma, false, 0.5, false); for (TopExp_Explorer k(golgeBirikim, TopAbs_FACE); k.More(); k.Next()) { const TopoDS_Face yuz = TopoDS::Face(k.Current()); TopLoc_Location loc; const Handle(Poly_Triangulation) tri = BRep_Tool::Triangulation(yuz, loc); if (tri.IsNull()) continue; const gp_Trsf t = loc.Transformation(); const u32 taban = (u32)konum.size(); const bool ters = yuz.Orientation() == TopAbs_REVERSED; for (int i = 1; i <= tri->NbNodes(); ++i) { const gp_Pnt p = tri->Node(i).Transformed(t); konum.push_back({(f32)p.X(), (f32)p.Y(), (f32)p.Z()}); } // Normal: overlay isiklandirmasi icin yuz normali (per-vertex ayni). normal.resize(konum.size(), {0, 0, 1}); { Standard_Real u0, u1, v0, v1; BRepTools::UVBounds(yuz, u0, u1, v0, v1); BRepAdaptor_Surface sf(yuz); gp_Pnt P; gp_Vec dU, dV; sf.D1(0.5 * (u0 + u1), 0.5 * (v0 + v1), P, dU, dV); gp_Vec nv = dU.Crossed(dV); if (nv.Magnitude() > 1e-12) { nv.Normalize(); if (ters) nv.Reverse(); for (size_t i = taban; i < konum.size(); ++i) normal[i] = {(f32)nv.X(), (f32)nv.Y(), (f32)nv.Z()}; } } for (int i = 1; i <= tri->NbTriangles(); ++i) { int a, b, c; tri->Triangle(i).Get(a, b, c); if (ters) std::swap(b, c); // disa bakan sarim indeks.push_back(taban + (u32)(a - 1)); indeks.push_back(taban + (u32)(b - 1)); indeks.push_back(taban + (u32)(c - 1)); } } return !indeks.empty(); } catch (const Standard_Failure& h) { gunluk::uyari("undercutGolgeMesh istisna: {}", h.what()); konum.clear(); normal.clear(); indeks.clear(); return false; } } // ---- Analiz WB (Dalga B): Kurvatur (egrilik) ------------------------------ namespace { // 1-tabanli ordinalin TopoDS alt-seklini bulur (brep.cpp ordinalBul ikizi; o // anonim namespace'te — TU disi erisemez). bool ordinalAltSekil(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; } } // namespace u32 yuzKalinlikOrnekle(const BrepGovde& g, u32 ordinal, u32 maksIzgara, f64 enBuyuk, std::vector& noktalar, std::vector& kalinliklar) { if (!g.gecerliMi() || ordinal == 0) return 0; occtSinyalKur(); u32 eklendi = 0; try { TopoDS_Shape s; if (!ordinalAltSekil(sekil(g), TopAbs_FACE, ordinal, s)) return 0; const TopoDS_Face yuz = TopoDS::Face(s); Standard_Real u0, u1, v0, v1; BRepTools::UVBounds(yuz, u0, u1, v0, v1); if (!(u1 > u0) || !(v1 > v0)) return 0; BRepAdaptor_Surface sf(yuz); BRepClass_FaceClassifier sinif; const f64 tol = std::max(BRep_Tool::Tolerance(yuz), 1e-7); // Duz yuzde sabit normal (yuzBilgi = draftRapor/undercut ile AYNI kaynak; // gercek STEP'te kendi dU×dV hesabi ZIT isaret verebiliyor — undercutRapor // yorumu). Egri yuzde nokta basi yuzNoktaNormali. YuzBilgi yb; const bool ybVar = g.yuzBilgi(ordinal, yb, false); const bool duz = ybVar && yb.duzlemMi && yb.normal.uzunlukKare() >= 1e-12; const u32 n = std::clamp(maksIzgara, 2u, 64u); // TRIM KISAYOLU (govdeKalinlikOrnekle ile ayni gerekce + olcu): kirpma yoksa // BRepClass_FaceClassifier hucre basina agac gezintisi yapar ama HICBIR ornegi // elemez → kutu/plaka/prizma yuzlerinde saf israf. Alan karsilastirmasiyla // atlanir. Kirpma olan yuzlerde yine klasifre edilir (davranis birebir). bool sinifGerekli = true; { GProp_GProps tamAlanProp, trimAlanProp; BRepGProp::SurfaceProperties(yuz, tamAlanProp); BRepGProp::SurfaceProperties(yuz, trimAlanProp, tol); const f64 tamAlan = tamAlanProp.Mass(), trimAlan = trimAlanProp.Mass(); sinifGerekli = !(trimAlan > 0.0 && tamAlan > 0.0 && trimAlan >= tamAlan * 0.995); } // Isin-kesisim govde yuku BIR kez (n² ornek ayni yuklu nesneyi kullanir). BRepIntCurveSurface_Inter kesisim; kesisim.Load(sekil(g), 1e-6); for (u32 iu = 0; iu < n; ++iu) for (u32 iv = 0; iv < n; ++iv) { // Hucre MERKEZLERI: kenarda ornekleme trim sinirinda kararsiz + kose // isini komsu yuzu siyirip iskaliyor (olculdu). const f64 u = u0 + (u1 - u0) * ((f64)iu + 0.5) / (f64)n; const f64 v = v0 + (v1 - v0) * ((f64)iv + 0.5) / (f64)n; if (sinifGerekli) { sinif.Perform(yuz, gp_Pnt2d(u, v), tol); if (sinif.State() != TopAbs_IN) continue; // trim disi (delik/kirpik yuz) } const gp_Pnt P = sf.Value(u, v); const Vec3d p{P.X(), P.Y(), P.Z()}; Vec3d nrm = yb.normal; if (!duz && !g.yuzNoktaNormali(ordinal, p, nrm)) continue; if (nrm.uzunlukKare() < 1e-12) continue; const f64 k = noktaKalinlikYuklu(kesisim, p, nrm, enBuyuk); if (k <= 0) continue; noktalar.push_back(p); kalinliklar.push_back(k); ++eklendi; } } catch (const Standard_Failure& h) { gunluk::uyari("yuzKalinlikOrnekle istisna: {}", h.what()); } return eklendi; } u32 govdeKalinlikOrnekle(const BrepGovde& g, u32 maksIzgara, f64 enBuyuk, std::vector& noktalar, std::vector& kalinliklar, std::vector& yuzOrdinalleri) { if (!g.gecerliMi()) return 0; occtSinyalKur(); u32 eklendi = 0; try { const TopoDS_Shape& sek = sekil(g); // TEK yukleme — tum yuzler bunu paylasir (asagida yuz basina tekrar Load vardi). BRepIntCurveSurface_Inter kesisim; kesisim.Load(sek, 1e-6); const u32 n = std::clamp(maksIzgara, 2u, 64u); u32 ord = 0; for (TopExp_Explorer k(sek, TopAbs_FACE); k.More(); k.Next()) { ++ord; const TopoDS_Face yuz = TopoDS::Face(k.Current()); Standard_Real u0, u1, v0, v1; BRepTools::UVBounds(yuz, u0, u1, v0, v1); if (!(u1 > u0) || !(v1 > v0)) continue; BRepAdaptor_Surface sf(yuz); const f64 tol = std::max(BRep_Tool::Tolerance(yuz), 1e-7); YuzBilgi yb; const bool ybVar = g.yuzBilgi(ord, yb, false); const bool duz = ybVar && yb.duzlemMi && yb.normal.uzunlukKare() >= 1e-12; // TRIM KISAYOLU: BRepClass_FaceClassifier::Perform hucre BASINA bir agac // gezintisi (edge walk) yapıyor — duz kutu/plaka yuzunde trim tam yuzu sarar, // hicbir UV hucre merkezi disarida degildir, yani sonuc Hep TopAbs_IN'dir. // O durumda classifier HICBIR ORNEK elmez; atlanir (kutu/plaka/prizma yuzleri // = kalinlik analizinin yogun oldugu yer). Kirpma olan yuzlerde yine // klasifre edilir — davranis birebir korunur. // Alan karsilastirmasi: trim alani yuz alanine ~esitse hicbir hucre disari // dusmez (BRepGProp yuz alani = trim alani). BRepClass_FaceClassifier sinif; bool sinifGerekli = true; { GProp_GProps yuzAlanProp, trimAlanProp; BRepGProp::SurfaceProperties(yuz, yuzAlanProp); BRepGProp::SurfaceProperties(yuz, trimAlanProp, tol); // trim'e gore const f64 tamAlan = yuzAlanProp.Mass(); const f64 trimAlan = trimAlanProp.Mass(); // %0.5'ten kucuk fark → kirpma yok sayilir. Alan tek duzlem oldugunda // bu oran 1'dir; delikli/ayri bolgulu yuzlerde <1 olur ve klasifre calisir. sinifGerekli = !(trimAlan > 0.0 && tamAlan > 0.0 && trimAlan >= tamAlan * 0.995); } for (u32 iu = 0; iu < n; ++iu) for (u32 iv = 0; iv < n; ++iv) { const f64 u = u0 + (u1 - u0) * ((f64)iu + 0.5) / (f64)n; const f64 v = v0 + (v1 - v0) * ((f64)iv + 0.5) / (f64)n; if (sinifGerekli) { sinif.Perform(yuz, gp_Pnt2d(u, v), tol); if (sinif.State() != TopAbs_IN) continue; // trim disi } const gp_Pnt P = sf.Value(u, v); const Vec3d p{P.X(), P.Y(), P.Z()}; Vec3d nrm = yb.normal; if (!duz && !g.yuzNoktaNormali(ord, p, nrm)) continue; if (nrm.uzunlukKare() < 1e-12) continue; const f64 k = noktaKalinlikYuklu(kesisim, p, nrm, enBuyuk); if (k <= 0) continue; noktalar.push_back(p); kalinliklar.push_back(k); yuzOrdinalleri.push_back(ord); ++eklendi; } } } catch (const Standard_Failure& h) { gunluk::uyari("govdeKalinlikOrnekle istisna: {}", h.what()); } return eklendi; } namespace { KurvaturBilgi kurvaturHesap(const TopoDS_Face& yuz, f64 u, f64 v) { KurvaturBilgi r; try { // Cozunurluk: OCCT hesap toleransi (mm cinsinden makul). BRepLProp_SLProps prop(BRepAdaptor_Surface(yuz), u, v, 2, 1e-6); if (!prop.IsCurvatureDefined()) return r; r.gauss = prop.GaussianCurvature(); r.ortalama = prop.MeanCurvature(); r.kMin = prop.MinCurvature(); // asal (principal) egrilik k1 r.kMaks = prop.MaxCurvature(); // asal egrilik k2 r.gecerli = true; } catch (const Standard_Failure&) { r = KurvaturBilgi{}; } return r; } } // namespace KurvaturBilgi yuzKurvatur(const BrepGovde& g, u32 ordinal, f64 u, f64 v) { KurvaturBilgi r; if (!g.gecerliMi()) return r; TopoDS_Shape s; if (!ordinalAltSekil(sekil(g), TopAbs_FACE, ordinal, s)) return r; return kurvaturHesap(TopoDS::Face(s), u, v); } KurvaturBilgi yuzKurvaturMerkez(const BrepGovde& g, u32 ordinal) { KurvaturBilgi r; if (!g.gecerliMi()) return r; TopoDS_Shape s; if (!ordinalAltSekil(sekil(g), TopAbs_FACE, ordinal, s)) return r; try { const TopoDS_Face yuz = TopoDS::Face(s); BRepAdaptor_Surface yuzey(yuz); const f64 u = 0.5 * (yuzey.FirstUParameter() + yuzey.LastUParameter()); const f64 v = 0.5 * (yuzey.FirstVParameter() + yuzey.LastVParameter()); return kurvaturHesap(yuz, u, v); } catch (const Standard_Failure&) { return r; } } KenarKurvatur kenarKurvatur(const BrepGovde& g, u32 ordinal, f64 t01) { KenarKurvatur r; if (!g.gecerliMi()) return r; TopoDS_Shape s; if (!ordinalAltSekil(sekil(g), TopAbs_EDGE, ordinal, s)) return r; try { BRepAdaptor_Curve egri(TopoDS::Edge(s)); const f64 u0 = egri.FirstParameter(), u1 = egri.LastParameter(); const f64 t = u0 + std::clamp(t01, 0.0, 1.0) * (u1 - u0); BRepLProp_CLProps prop(egri, t, 2, 1e-6); const gp_Pnt p = prop.Value(); r.nokta = {p.X(), p.Y(), p.Z()}; r.egrilik = prop.Curvature(); // >=0 if (prop.Curvature() > 1e-9) { gp_Dir nd; prop.Normal(nd); // egrilik (asal normal) yonu → tarak ignesi yonu r.yon = {nd.X(), nd.Y(), nd.Z()}; } r.gecerli = true; } catch (const Standard_Failure&) { r = KenarKurvatur{}; } return r; } } // namespace fiko::geometri