#include "brep_ic.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fiko/core/gunluk.hpp" namespace fiko::geometri { using ic::sar; using ic::sekil; namespace { // Bir sekle atanmis rengi XCAF renk aracindan okur (yuzey, sonra genel) bool renkOku(const Handle(XCAFDoc_ColorTool)& renkArac, const TopoDS_Shape& s, Vec3f& renk) { Quantity_Color c; if (renkArac->GetColor(s, XCAFDoc_ColorSurf, c) || renkArac->GetColor(s, XCAFDoc_ColorGen, c)) { renk = {(f32)c.Red(), (f32)c.Green(), (f32)c.Blue()}; return true; } return false; } // Bir etikete atanmis rengi XCAF renk aracindan okur (yuzey, sonra genel) bool renkOkuEtiket(const Handle(XCAFDoc_ColorTool)& renkArac, const TDF_Label& e, Vec3f& renk) { Quantity_Color c; if (renkArac->GetColor(e, XCAFDoc_ColorSurf, c) || renkArac->GetColor(e, XCAFDoc_ColorGen, c)) { renk = {(f32)c.Red(), (f32)c.Green(), (f32)c.Blue()}; return true; } return false; } // Govdenin yuzlerini ucgenle ile AYNI sirada (TopExp_Explorer FACE) gezip yuz-basi // rengini cozer; en az bir yuz ayri renkliyse listeyi govdeye atar (renksiz yuzler // tabanRenk'e duser). Tumu tekduzeyse dokunmaz (Ozellikler paneli renk duzenlemesi // icin). Renk okumasi orijinal (dunyaya tasinmamis) sekil uzerinden yapilmali — // XCAF renkleri prototip yuzlerine bagli. void yuzRenkleriCoz(const Handle(XCAFDoc_ColorTool)& renkArac, const TopoDS_Shape& govdeSekil, BrepGovde& govde, bool renkVar, Vec3f renk) { const Vec3f taban = renkVar ? renk : Vec3f{0.62f, 0.66f, 0.72f}; // varsayilan gri std::vector yuzR; bool ayriRenkVar = false; for (TopExp_Explorer f(govdeSekil, TopAbs_FACE); f.More(); f.Next()) { Vec3f fr; if (renkOku(renkArac, f.Current(), fr)) { yuzR.push_back(fr); ayriRenkVar = true; } else yuzR.push_back(taban); // yuzun kendi rengi yok → govde/varsayilan renge dus } // Renkleri brep'e YAZ: yüz-seviyesi ayrı renk VEYA gövde-seviyesi renk varsa. Böylece brep // hep renkli → tüm işlemlerde (push/pull, fillet, draft, boolean) renkPropagasyonu çalışır // ve renk yüz ordinali kayınca da OCCT topoloji izlemeyle taşınır (renk kaybı kök-çözümü). if (ayriRenkVar || renkVar) govde.yuzRenkleriAta(std::move(yuzR)); } void yuzRenkleriCoz(const Handle(XCAFDoc_ColorTool)& renkArac, const TopoDS_Shape& govdeSekil, IthalGovde& ig) { yuzRenkleriCoz(renkArac, govdeSekil, ig.govde, ig.renkVar, ig.renk); } } std::vector stepYukle(const std::string& yol, const IlerlemeGeri& ilerle) { const auto rapor = [&](f32 o, std::string_view m) { if (ilerle) ilerle(o, m); }; std::vector sonuc; try { // OCCT STEP okuyucu Interface_Static global durumunu kullanir; ayni anda iki // parse'i onlemek icin serilesir (worker havuzunda guvenli). static std::mutex occtKilit; std::lock_guard occtGuard(occtKilit); rapor(0.03f, "STEP okunuyor"); Handle(TDocStd_Document) belge = new TDocStd_Document("MDTV-XCAF"); STEPCAFControl_Reader okuyucu; okuyucu.SetColorMode(true); okuyucu.SetNameMode(true); const IFSelect_ReturnStatus okumaDurum = okuyucu.ReadFile(yol.c_str()); if (okumaDurum != IFSelect_RetDone) { // TESHIS: durum kodu eklendi — RetError(1)=dosya/format hatasi (bozuk STEP, // yanlis uzanti, erisim izni), RetFail(3)=OCCT ic hatasi, RetVoid(0)=hic // entity okunamadi (bos/gecersiz dosya). "okunamadi" tek basina teshis edilemezdi. const char* durumAd = okumaDurum == IFSelect_RetVoid ? "RetVoid(bos/gecersiz)" : okumaDurum == IFSelect_RetError ? "RetError(dosya/format hatasi)" : okumaDurum == IFSelect_RetFail ? "RetFail(OCCT ic hatasi)" : okumaDurum == IFSelect_RetStop ? "RetStop" : "bilinmeyen"; gunluk::hata("STEP okunamadi: {} — durum: {} ({})", yol, (int)okumaDurum, durumAd); return sonuc; } rapor(0.2f, "Aktariliyor"); if (!okuyucu.Transfer(belge)) { gunluk::hata("STEP aktarilamadi (Transfer basarisiz — dosya OCCT'ye gore gecerli STEP " "ama XDE'ye aktarilamadi, olasi desteklenmeyen sema/entity): {}", yol); return sonuc; } rapor(0.42f, "Govdeler cozuluyor"); Handle(XCAFDoc_ShapeTool) sekilArac = XCAFDoc_DocumentTool::ShapeTool(belge->Main()); Handle(XCAFDoc_ColorTool) renkArac = XCAFDoc_DocumentTool::ColorTool(belge->Main()); TDF_LabelSequence etiketler; sekilArac->GetFreeShapes(etiketler); const int etiketSayi = etiketler.Length(); for (int i = 1; i <= etiketSayi; ++i) { rapor(0.42f + 0.06f * (f32)i / (f32)std::max(etiketSayi, 1), "Govdeler cozuluyor"); const TopoDS_Shape sekil = sekilArac->GetShape(etiketler.Value(i)); if (sekil.IsNull()) continue; Vec3f serbestRenk{}; const bool serbestRenkVar = renkOku(renkArac, sekil, serbestRenk); // Katilari ayri govde yap; her katinin kendi rengini (yoksa ust rengi) al bool katiBulundu = false; TopTools_MapOfShape katiKabuklari; // solid'lere ait shell'ler (asagida tekrar govde yapilmasin) for (TopExp_Explorer k(sekil, TopAbs_SOLID); k.More(); k.Next()) { katiBulundu = true; for (TopExp_Explorer sh(k.Current(), TopAbs_SHELL); sh.More(); sh.Next()) katiKabuklari.Add(sh.Current()); IthalGovde ig; ig.govde = sar(k.Current()); Vec3f r{}; if (renkOku(renkArac, k.Current(), r)) { ig.renk = r; ig.renkVar = true; } else if (serbestRenkVar) { ig.renk = serbestRenk; ig.renkVar = true; } yuzRenkleriCoz(renkArac, k.Current(), ig); sonuc.push_back(std::move(ig)); } // Solid'e ait OLMAYAN serbest kabuklar (surfacic/construction geometri) — solid'in // yaninda ayri yuzey govdeleri STEP'te sik gorulur; eskiden burada sessizce // atlanip parca "yarim" acilirdi. for (TopExp_Explorer sh(sekil, TopAbs_SHELL); sh.More(); sh.Next()) { if (katiKabuklari.Contains(sh.Current())) continue; katiBulundu = true; IthalGovde ig; ig.govde = sar(sh.Current()); Vec3f r{}; if (renkOku(renkArac, sh.Current(), r)) { ig.renk = r; ig.renkVar = true; } else if (serbestRenkVar) { ig.renk = serbestRenk; ig.renkVar = true; } yuzRenkleriCoz(renkArac, sh.Current(), ig); sonuc.push_back(std::move(ig)); } if (!katiBulundu) { IthalGovde ig; ig.govde = sar(sekil); ig.renk = serbestRenk; ig.renkVar = serbestRenkVar; yuzRenkleriCoz(renkArac, sekil, ig); sonuc.push_back(std::move(ig)); } } gunluk::bilgi("STEP yuklendi: {} govde ({})", sonuc.size(), yol); } catch (const Standard_Failure& hata) { gunluk::hata("STEP yukleme hatasi: {}", hata.what()); } return sonuc; } namespace { // OCCT gercek ad yoksa otomatik ad uydurur: referans/occurrence → "=>[0:1:1:3]" // veya "[0:1:1:3]" (etiket yolu); adsiz free-shape → sekil TIPI ("COMPOUND", // "SOLID"...). Bunlarin hicbiri anlamli ad DEGIL → ele. bool otoEtiketAdi(const std::string& s) { if (s.rfind("=>", 0) == 0) return true; // "=>[...]" if (s.size() >= 3 && s[0] == '[' && s[1] == '0' && s[2] == ':') return true; // "[0:1:..]" static const char* tipler[] = {"COMPOUND", "COMPSOLID", "SOLID", "SHELL", "FACE", "WIRE", "EDGE", "VERTEX", "SHAPE"}; for (const char* t : tipler) if (s == t) return true; return false; } // STEP entity adini (MANIFOLD_SOLID_BREP vb. RepresentationItem.Name) sekilden oku. // XDE alt-etiketlerinde ad tutulmadigi durumda gercek body adini ("PartBody", // "Body.6"...) STEP modelinden getirir. Bulunamazsa "". // NOT (bkz. 17778_a09013_m01_01.stp teshisi): EntityFromShapeResult (mode=0 DAHIL, // yani en ucuz/dogrudan yol bile) bu STEP'in coklu-solid (NAUO'suz, montaj yapisi // olmayan) bazi govdelerinde OCCT'nin transfer-okuyucu ic durumunda // "resource deadlock would occur" (std::system_error, kod 36) firlatiyor. Hangi // modun kullanildigindan BAGIMSIZ oldugu dogrulandi (mode=3→mode=0 sirasi // degistirilmesi sorunu COZMEDI) — bu, XSControl_TransferReader/Interface // altyapisinin bu dosyaya ozgu bir ic kirilganligi, cagiran tarafta // duzeltilemez. Bu yuzden cagri BURADA izole edilir: basarisiz olursa (adsiz) // gövde numaralı fallback'e ("Gövde N") duser — cagiran (hiyerarsiGez) zaten // bunu graceful sekilde ele aliyor; tek govde biraz daha az aciklayici // isimlenir ama TUM STEP ithalati coplmez. std::string stepSekilAdi(const Handle(XSControl_TransferReader)& tr, const TopoDS_Shape& s) { if (tr.IsNull() || s.IsNull()) return {}; try { Handle(Standard_Transient) ent = tr->EntityFromShapeResult(s, 0); // dogrudan (ucuz) Handle(StepRepr_RepresentationItem) oge = Handle(StepRepr_RepresentationItem)::DownCast(ent); if (oge.IsNull() || oge->Name().IsNull()) return {}; std::string ad(oge->Name()->ToCString()); // Bas/son bosluklari kirp (CATIA bazen " " yazar). const size_t b = ad.find_first_not_of(" \t"); const size_t e = ad.find_last_not_of(" \t"); if (b == std::string::npos) return {}; return ad.substr(b, e - b + 1); } catch (const Standard_Failure& hata) { gunluk::hata("STEP entity adi okunamadi (Standard_Failure, adsiz gövdeye dusuluyor): {}", hata.GetMessageString()); return {}; } catch (const std::exception& hata) { // ör. std::system_error("resource deadlock would occur") — bkz. yukaridaki NOT. gunluk::hata("STEP entity adi okunamadi (std::exception, adsiz gövdeye dusuluyor): {}", hata.what()); return {}; } } // Etiketin XCAF adini (TDataStd_Name) UTF-8 std::string olarak oku; yoksa / OCCT // oto-adi ("=>[...]") ise "". std::string etiketAdi(const TDF_Label& etiket) { Handle(TDataStd_Name) isim; if (etiket.FindAttribute(TDataStd_Name::GetID(), isim)) { TCollection_AsciiString utf8(isim->Get(), '?'); std::string s(utf8.ToCString(), (size_t)utf8.Length()); if (otoEtiketAdi(s)) return {}; return s; } return {}; } // DFS azami derinligi: bundan sonrasi kesilir. Gercek montajlar bu derinlige // neredeyse hicbir zaman ulasmaz; bu deger yalniz patolojik/cevrimsel montajlara // karsi bir GUVENLIK TAVANI'dir. // NEDEN GEREKLI: yigin tasmasi (stack overflow) gercek-zamanli bir guard-page // ihlalidir ve occtSinyalKur()/OSD::SetSignal BUNU GUVENILIR SEKILDE YAKALAYAMAZ — // SEH isleyicisi calistiginda stack zaten tukenmis olur; _resetstkoflw() ile guard // page onceden geri kurulmadan devam eden herhangi bir derin cagri (or. yuzlerce // hiyerarsiGez cercevesinin unwind'i, ya da CRT'nin kendi ic kilidi) IKINCI, // KURTARILAMAZ bir hataya yol acar — pratikte "resource deadlock would occur" // (std::system_error) olarak yuzeye cikar, cunku bozulmus stack/kilit durumunda // CRT mutex sarmalayicisi ayni thread'de "ikinci kilit" sanip hata firlatir. // Bu yuzden savunma BURADA, girdi seviyesinde olmali — SEH/sinyal katmaninda // duzeltilemez. constexpr int HIYERARSI_MAKS_DERINLIK = 300; // Montaj hiyerarsisini DFS ile gezip IthalDugum agaci uretir. // birikenLoc: kokten bu etikete kadar biriken instance donusumu (prototip→dunya). // derinlik/yol: asiri derin VEYA cevrimsel (kendine referans veren) montajlarda // yigin tasmasini onlemek icin dal kesme amacli (bkz. HIYERARSI_MAKS_DERINLIK). IthalDugum hiyerarsiGez(const Handle(XCAFDoc_ShapeTool)& sekilArac, const Handle(XCAFDoc_ColorTool)& renkArac, const Handle(XSControl_TransferReader)& tr, const TDF_Label& etiket, const TopLoc_Location& birikenLoc, int derinlik = 0, std::vector* yol = nullptr) { IthalDugum dugum; dugum.ad = etiketAdi(etiket); if (derinlik > HIYERARSI_MAKS_DERINLIK) { gunluk::hata("STEP hiyerarsi: azami derinlik ({}) asildi, dal kesildi ({})", HIYERARSI_MAKS_DERINLIK, dugum.ad); return dugum; } std::vector yerelYol; if (!yol) yol = &yerelYol; for (const TDF_Label& ata : *yol) { if (ata.IsEqual(etiket)) { gunluk::hata("STEP hiyerarsi: cevrimsel montaj referansi tespit edildi, dal kesildi ({})", dugum.ad); return dugum; } } yol->push_back(etiket); struct YolTemizleyici { std::vector* y; ~YolTemizleyici() { y->pop_back(); } } yolTemizleyici{yol}; // TESHIS: hangi etiket/derinlikte neyin patladigini bulmak icin gecici forensic // sarmalayici (bkz. 17778_a09013_m01_01.stp — "resource deadlock would occur"). // Derinlik guard'i VE 16MB yigin tavani sonuca ETKI ETMEDI → bu, gercek bir // stack overflow DEGIL; std::system_error(resource_deadlock) baska bir // mekanizmadan (ör. yeniden-girisimli kilit) geliyor. Konumu netlesince bu // try/catch KALDIRILACAK. try { // TESHIS-ADIM: her alt-adimi ayri sarar; std::system_error bu adimda cikarsa // TAM OLARAK hangi OCCT cagrisi oldugunu loglar (genel derinlik/etiket logundan // daha hassas). Adim adi + derinlik + o anki dugum.ad ile. auto adimTeshis = [&](const char* adimAdi, auto&& fn) -> decltype(fn()) { try { return fn(); } catch (const std::system_error& se) { gunluk::hata( "STEP hiyerarsi TESHIS-ADIM: adim='{}' derinlik={} etiket='{}' kod={} " "kategori={} mesaj={}", adimAdi, derinlik, dugum.ad, se.code().value(), se.code().category().name(), se.what()); throw; } }; // Referans (instance) etiketi ise prototipe yonlendir; renk/ad instance'ta olabilir. TDF_Label hedef = etiket; const bool referansMi = adimTeshis( "IsReference", [&] { return XCAFDoc_ShapeTool::IsReference(etiket); }); if (referansMi) { TDF_Label prototip; const bool bulunduMu = adimTeshis("GetReferredShape", [&] { return XCAFDoc_ShapeTool::GetReferredShape(etiket, prototip); }); if (bulunduMu) hedef = prototip; } // Instance adi yoksa/oto-ad ise prototip (product) adini kullan. if (dugum.ad.empty()) dugum.ad = adimTeshis("etiketAdi(hedef)", [&] { return etiketAdi(hedef); }); const bool assemblyMi = adimTeshis("IsAssembly", [&] { return XCAFDoc_ShapeTool::IsAssembly(hedef); }); if (assemblyMi) { TDF_LabelSequence bilesenler; adimTeshis("GetComponents", [&] { XCAFDoc_ShapeTool::GetComponents(hedef, bilesenler); }); for (int i = 1; i <= bilesenler.Length(); ++i) { const TDF_Label bilesen = bilesenler.Value(i); // Bilesenin yerel instance konumunu biriktir: dunya = birikenLoc * bilesenLoc const TopLoc_Location yerel = adimTeshis("GetLocation", [&] { return XCAFDoc_ShapeTool::GetLocation(bilesen); }); IthalDugum alt = hiyerarsiGez(sekilArac, renkArac, tr, bilesen, birikenLoc.Multiplied(yerel), derinlik + 1, yol); // Bilesen adi bos ise prototip adini dene if (alt.ad.empty()) alt.ad = etiketAdi(bilesen); dugum.altlar.push_back(std::move(alt)); } return dugum; } // Yaprak (simple shape): prototip geometrisini biriken konumla dunyaya tasi. const TopoDS_Shape prototip = adimTeshis("GetShape", [&] { return sekilArac->GetShape(hedef); }); if (prototip.IsNull()) return dugum; TopoDS_Shape sekil = prototip; adimTeshis("Move", [&] { sekil.Move(birikenLoc); }); // dunya koordinati (cagiran ekstra donusum uygulamaz) Vec3f renk{}; bool renkVar = adimTeshis("renkOkuEtiket(etiket)", [&] { return renkOkuEtiket(renkArac, etiket, renk); }) || adimTeshis("renkOkuEtiket(hedef)", [&] { return renkOkuEtiket(renkArac, hedef, renk); }); // Montaj yapisi olmayip TEK yaprakta birden fazla SOLID iceren STEP (ör. 5 parca // tek product altinda, NAUO yok) → her katiyi ayri parca dugumu yap; boylece // urun agacinda ve sahnede parcalar ayrilir (flat stepYukle ile ayni davranis). // Renk prototip (tasinmamis) kati uzerinden okunur — XCAF renkleri konuma bagli. int katiSayi = 0; adimTeshis("SolidSay", [&] { for (TopExp_Explorer k(sekil, TopAbs_SOLID); k.More(); k.Next()) ++katiSayi; }); if (katiSayi >= 2) { TopExp_Explorer kp(prototip, TopAbs_SOLID); // renk/ad: tasinmamis kati (ayni sira) int idx = 0; for (TopExp_Explorer k(sekil, TopAbs_SOLID); k.More() && kp.More(); k.Next(), kp.Next()) { IthalDugum alt; ++idx; // Katinin gercek adini ("PartBody", "Body.6"...) STEP modelinden oku // (MANIFOLD_SOLID_BREP.Name). Yoksa parca/"Gövde" adi + sira ile numarala. // NOT: BILEREK adimTeshis() UZERINDEN CAGRILMIYOR. stepSekilAdi kendi icinde // std::exception'i (dahil system_error/"resource deadlock") yakalayip "" donuyor // GIBI GORUNUYOR, ama teshis loglari gosterdi ki bozulmus stack/kilit durumunda // O catch bloğunun kendisi (unwind sirasinda CRT/RTTI kilit mekanizmasi) IKINCI // bir system_error firlatabiliyor — bu da stepSekilAdi'nin kendi try/catch'ini // atlayip adimTeshis'e sizardi, adimTeshis da throw; ile hiyerarsiGez'i cokertip // (bkz. yukaridaki disaridaki try/catch) TUM kokun hiyerarsisini basarisiz // sayardi — GEOMETRISI SAGLAM olan kardes katilar dahil. Isim okuma bir // "nice-to-have"; bir tek katinin adi cozulemedigi icin butun urun agacinin // cokmesine izin VERILMEMELI. Bu yuzden ikinci bir izolasyon katmani burada, // cagri yerinde: hata olursa (ne olursa olsun) sadece bu katinin adi bos // kalir, numarali fallback'e duser, kardesler ve dugum etkilenmez. std::string katiAd; try { katiAd = stepSekilAdi(tr, kp.Current()); } catch (const std::exception& hata) { gunluk::hata( "STEP hiyerarsi: kati adi cozulurken cagri-yeri istisnasi (izole edildi, " "numarali fallback'e dusuluyor) derinlik={} etiket='{}' idx={} mesaj={}", derinlik, dugum.ad, idx, hata.what()); } alt.ad = !katiAd.empty() ? katiAd : (dugum.ad.empty() ? std::string("Gövde") : dugum.ad) + " " + std::to_string(idx); Vec3f kr{}; bool krVar = adimTeshis("renkOku(kati)", [&] { return renkOku(renkArac, kp.Current(), kr); }); if (!krVar) { kr = renk; krVar = renkVar; } alt.govde = adimTeshis("sar(kati)", [&] { return sar(k.Current()); }); alt.govdeVar = alt.govde.gecerliMi(); alt.renk = kr; alt.renkVar = krVar; adimTeshis("yuzRenkleriCoz(kati)", [&] { yuzRenkleriCoz(renkArac, kp.Current(), alt.govde, krVar, kr); }); dugum.altlar.push_back(std::move(alt)); } // Solid'e ait OLMAYAN serbest kabuklar (surfacic/construction geometri) — solid'lerin // yaninda ayri yuzey govdeleri STEP'te sik gorulur; eskiden burada sessizce atlanip // parca "yarim" acilirdi (bkz. brep_io.cpp diagnostigi). TopTools_MapOfShape katiKabuklari; for (TopExp_Explorer k(sekil, TopAbs_SOLID); k.More(); k.Next()) for (TopExp_Explorer sh(k.Current(), TopAbs_SHELL); sh.More(); sh.Next()) katiKabuklari.Add(sh.Current()); TopExp_Explorer shp(prototip, TopAbs_SHELL); for (TopExp_Explorer sh(sekil, TopAbs_SHELL); sh.More() && shp.More(); sh.Next(), shp.Next()) { if (katiKabuklari.Contains(sh.Current())) continue; IthalDugum alt; ++idx; // NOT: ayni cagri-yeri izolasyonu (yukaridaki kati adi bloguna bkz.) — kabuk // adi okuma da tum kok'u cokertmemeli. std::string kabukAd; try { kabukAd = stepSekilAdi(tr, shp.Current()); } catch (const std::exception& hata) { gunluk::hata( "STEP hiyerarsi: kabuk adi cozulurken cagri-yeri istisnasi (izole edildi, " "numarali fallback'e dusuluyor) derinlik={} etiket='{}' idx={} mesaj={}", derinlik, dugum.ad, idx, hata.what()); } alt.ad = !kabukAd.empty() ? kabukAd : (dugum.ad.empty() ? std::string("Yuzey") : dugum.ad) + " " + std::to_string(idx); Vec3f kr{}; bool krVar = adimTeshis("renkOku(kabuk)", [&] { return renkOku(renkArac, shp.Current(), kr); }); if (!krVar) { kr = renk; krVar = renkVar; } alt.govde = adimTeshis("sar(kabuk)", [&] { return sar(sh.Current()); }); alt.govdeVar = alt.govde.gecerliMi(); alt.renk = kr; alt.renkVar = krVar; adimTeshis("yuzRenkleriCoz(kabuk)", [&] { yuzRenkleriCoz(renkArac, shp.Current(), alt.govde, krVar, kr); }); dugum.altlar.push_back(std::move(alt)); } return dugum; // bu dugum artik GRUP (govdeVar=false); parcalar altinda } // Tek kati yaprak: XDE adi yoksa STEP modelinden gercek sekil adini dene. if (dugum.ad.empty()) dugum.ad = adimTeshis("stepSekilAdi", [&] { return stepSekilAdi(tr, prototip); }); dugum.govde = adimTeshis("sar", [&] { return sar(sekil); }); dugum.govdeVar = dugum.govde.gecerliMi(); dugum.renk = renk; dugum.renkVar = renkVar; // Yuz renkleri prototip (tasinmamis) sekle bagli — hedef sekli kullan. adimTeshis("yuzRenkleriCoz", [&] { yuzRenkleriCoz(renkArac, prototip, dugum.govde, renkVar, renk); }); return dugum; } catch (const std::system_error& se) { gunluk::hata( "STEP hiyerarsi TESHIS: system_error derinlik={} etiket='{}' assembly={} " "kod={} kategori={} mesaj={}", derinlik, dugum.ad, XCAFDoc_ShapeTool::IsAssembly(etiket), se.code().value(), se.code().category().name(), se.what()); throw; } catch (const Standard_Failure&) { throw; } catch (const std::exception& e) { gunluk::hata("STEP hiyerarsi TESHIS: std::exception derinlik={} etiket='{}' mesaj={}", derinlik, dugum.ad, e.what()); throw; } } } std::vector stepYukleHiyerarsi(const std::string& yol, const IlerlemeGeri& ilerle) { const auto rapor = [&](f32 o, std::string_view m) { if (ilerle) ilerle(o, m); }; std::vector sonuc; try { static std::mutex occtKilit; std::lock_guard occtGuard(occtKilit); rapor(0.03f, "STEP okunuyor"); // NOT: read.stepcaf.subshapes.name=1 body adlarini XDE'ye getirir AMA alt-sekil // genisletmesi bazi STEP'lerde RENKLERI bozar. Bu yuzden KULLANMIYORUZ; body adi // belge yapisini degistirmeden STEP modelinden okunur (bkz. stepSekilAdi). Handle(TDocStd_Document) belge = new TDocStd_Document("MDTV-XCAF"); STEPCAFControl_Reader okuyucu; okuyucu.SetColorMode(true); okuyucu.SetNameMode(true); const IFSelect_ReturnStatus okumaDurum = okuyucu.ReadFile(yol.c_str()); if (okumaDurum != IFSelect_RetDone) { const char* durumAd = okumaDurum == IFSelect_RetVoid ? "RetVoid(bos/gecersiz)" : okumaDurum == IFSelect_RetError ? "RetError(dosya/format hatasi)" : okumaDurum == IFSelect_RetFail ? "RetFail(OCCT ic hatasi)" : okumaDurum == IFSelect_RetStop ? "RetStop" : "bilinmeyen"; gunluk::hata("STEP okunamadi: {} — durum: {} ({})", yol, (int)okumaDurum, durumAd); return sonuc; } rapor(0.2f, "Aktariliyor"); if (!okuyucu.Transfer(belge)) { gunluk::hata("STEP aktarilamadi (Transfer basarisiz — dosya OCCT'ye gore gecerli STEP " "ama XDE'ye aktarilamadi, olasi desteklenmeyen sema/entity): {}", yol); return sonuc; } rapor(0.42f, "Hiyerarsi cozuluyor"); Handle(XCAFDoc_ShapeTool) sekilArac = XCAFDoc_DocumentTool::ShapeTool(belge->Main()); Handle(XCAFDoc_ColorTool) renkArac = XCAFDoc_DocumentTool::ColorTool(belge->Main()); // STEP body isimleri ("PartBody", "Body.6"...) XDE'ye girmez; shape→STEP-entity // ile modelden okunur (bkz. stepSekilAdi). Handle(XSControl_TransferReader) tr = okuyucu.Reader().WS()->TransferReader(); TDF_LabelSequence kokler; sekilArac->GetFreeShapes(kokler); const int kokSayi = kokler.Length(); // Her kok BAGIMSIZ try/catch ile cozulur: STEP montajinda bir bilesenin // (bozuk/uc-durum geometri) Standard_Failure atmasi butun dosyayi degil, // yalniz o kok'u basarisiz yapsin — geri kalan parcalar yine acilsin. // Basarisiz kok'lar govdeVar=false + boş IthalDugum olarak eklenir; bu yuzden // sonuc.empty() TEK BASINA "hicbir sey acilmadi" testi icin YETERLI DEGIL — // cagiran yaprak (govdeVar=true) sayisini kontrol etmeli. int basarisizKok = 0; for (int i = 1; i <= kokSayi; ++i) { rapor(0.42f + 0.5f * (f32)i / (f32)std::max(kokSayi, 1), "Hiyerarsi cozuluyor"); try { sonuc.push_back(hiyerarsiGez(sekilArac, renkArac, tr, kokler.Value(i), TopLoc_Location())); } catch (const Standard_Failure& hata) { ++basarisizKok; gunluk::hata("STEP kok {}/{} cozulemedi: {}", i, kokSayi, hata.what()); sonuc.push_back(IthalDugum{}); } catch (const std::exception& hata) { // Standard_Failure disi (ör. std::bad_alloc, std::system_error) — HIYERARSI_MAKS_DERINLIK // guardina ragmen beklenmedik bir durum sizarsa yine de sadece bu kok basarisiz // sayilsin, tum STEP yuklemesi çökmesin. ++basarisizKok; gunluk::hata("STEP kok {}/{} cozulemedi (std::exception): {}", i, kokSayi, hata.what()); sonuc.push_back(IthalDugum{}); } } rapor(1.0f, "Tamam"); if (basarisizKok > 0) gunluk::hata("STEP hiyerarsi kismi basarili: {}/{} kok cozulemedi ({})", basarisizKok, kokSayi, yol); gunluk::bilgi("STEP hiyerarsi yuklendi: {} kok, {} basarisiz ({})", sonuc.size(), basarisizKok, yol); } catch (const Standard_Failure& hata) { gunluk::hata("STEP hiyerarsi yukleme hatasi: {}", hata.what()); } catch (const std::exception& hata) { gunluk::hata("STEP hiyerarsi yukleme hatasi (std::exception): {}", hata.what()); } return sonuc; } BrepGovde govdeDerinKopya(const BrepGovde& g) { if (!g.gecerliMi()) return {}; try { // copyGeom=true, copyMesh=true: Geom + mevcut triangulation dahil tam kopya. BRepBuilderAPI_Copy kopya(sekil(g), true, true); if (!kopya.IsDone() || kopya.Shape().IsNull()) return {}; BrepGovde yeni = sar(kopya.Shape()); // Per-yuz renklerini koru — BRepBuilderAPI_Copy yuz sayisini/TopExp sirasini // korur (bkz. brep_feature.cpp:619). Aksi halde paralel-mesh oncesi kopyada // STEP yuz renkleri kaybolur → govde tek renge boyanir. if (g.yuzRenkliMi()) yeni.yuzRenkleriAta(g.yuzRenkleri()); return yeni; } catch (const Standard_Failure& hata) { gunluk::hata("BREP derin kopya hatasi: {}", hata.what()); return {}; } } std::string govdeBaytYaz(const BrepGovde& g, bool ucgenli) { if (!g.gecerliMi()) return {}; try { std::ostringstream akis(std::ios::binary); // Ucgenleme (triangles): gercek B-rep'te GOMULMEZ (ucgenle yeniden uretir, dosya // kuculur). Mesh-temelli (VRML/STL yuzeysiz) gövdede ŞART — üçgenleme geometridir. BinTools::Write(sekil(g), akis, /*triangles*/ ucgenli, /*normals*/ false, BinTools_FormatVersion_CURRENT); return akis.str(); } catch (const Standard_Failure& hata) { gunluk::hata("BREP bayt yazma hatasi: {}", hata.what()); return {}; } } BrepGovde govdeBaytOku(const std::string& bayt) { if (bayt.empty()) return {}; try { std::istringstream akis(bayt, std::ios::binary); TopoDS_Shape s; BinTools::Read(s, akis); if (s.IsNull()) return {}; return sar(s); } catch (const Standard_Failure& hata) { gunluk::hata("BREP bayt okuma hatasi: {}", hata.what()); return {}; } } namespace { // Kaynak metinde `kelime`yi TAM SOZCUK olarak (harf/rakam/_ sinirlarinda) arar. // "Viewpoint" icindeki "point"i elemek icin sol/sag sinir denetimi yapar. size_t kelimeBul(const std::string& s, const char* kelime, size_t bas) { const size_t n = std::strlen(kelime); for (size_t p = s.find(kelime, bas); p != std::string::npos; p = s.find(kelime, p + 1)) { const bool solOk = (p == 0) || !(std::isalnum((unsigned char)s[p - 1]) || s[p - 1] == '_'); const size_t sag = p + n; const bool sagOk = (sag >= s.size()) || !(std::isalnum((unsigned char)s[sag]) || s[sag] == '_'); if (solOk && sagOk) return p; } return std::string::npos; } } // namespace // VRML V2.0 (utf8) mesh okuyucu — CATIA export .FikoCizim (icerik #VRML V2.0 utf8). // Her Shape'in IndexedFaceSet'i (yerel point + coordIndex) fan-triangule edilip tek // TopoDS_Compound'a toplanir. Dis Transform scale uygulanir; translation/rotation ve // materyal (diffuseColor) yok sayilir. Poligon yuzler ucgene bolunur. BrepGovde vrmlOku(const std::string& yol, KenarVeri* kenarlarCikti, std::vector* renklerCikti, std::vector* yuzShapeCikti, bool mmCevir) { try { std::ifstream dosya(yol, std::ios::binary | std::ios::ate); if (!dosya) { gunluk::hata("VRML acilamadi: {}", yol); return {}; } // Doğrudan string'e oku (stringstream + .str() 12MB'yi 2 kez kopyalıyordu ~100ms). const std::streamoff boyut = dosya.tellg(); std::string src; if (boyut > 0) { src.resize((size_t)boyut); dosya.seekg(0); dosya.read(src.data(), boyut); src.resize((size_t)dosya.gcount()); } // Dis olcek: ilk "scale" sozcugunden sonra 3 double (yoksa 1,1,1). double sx = 1.0, sy = 1.0, sz = 1.0; bool olcekVar = false; if (const size_t ps = kelimeBul(src, "scale", 0); ps != std::string::npos) olcekVar = std::sscanf(src.c_str() + ps + 5, " %lf %lf %lf", &sx, &sy, &sz) == 3; // mm dönüşümü: VRML birimi METRE → native mm. "scale" VARSA etkin = scale×1000 // (CATIA "scale 0.001" → 1.0: koordinatlar zaten mm sayıları, aynen geçer). // "scale" YOKSA koordinatlar mm kabul edilir (×1 — uyumsuz ama yaygın ihraççılar). if (mmCevir && olcekVar) { sx *= 1000.0; sy *= 1000.0; sz *= 1000.0; } if (mmCevir) gunluk::bilgi("VRML olcek: {} (mm donusumu {})", sx, olcekVar ? "scale×1000" : "scale yok, mm kabul"); // Materyal takibi (DEF/USE): "material DEF _ad Material { diffuseColor r g b }" // → matHaritasi[_ad]=renk + aktifRenk; "material USE _ad" → aktifRenk=matHaritasi[_ad]. // Her Shape'in yuzeyi o Shape'in aktifRenk'ini alir (Appearance geometriden ÖNCE gelir). std::unordered_map matHaritasi; Vec3f aktifRenk{0.62f, 0.66f, 0.72f}; // materyalsiz varsayilan gri TopoDS_Compound comp; BRep_Builder insaci; insaci.MakeCompound(comp); std::vector pts; // en son okunan "point" bloğu (aktif geometrinin) int yuzSayi = 0; u32 kenarOrdinal = 0; u32 aktifShape = 0; // her IndexedFaceSet'te artar → o Shape'in tum ucgenleri paylasir // Aktif geometri modu: IndexedFaceSet (yuz) mi IndexedLineSet (kenar) mi. Her // Shape'te ilgili anahtar coord/coordIndex'ten ÖNCE geldiği için mod doğru kurulur. bool cizgiModu = false; // "point" bloğunu (scaled gp_Pnt) oku; ']' sonrası konumu döndürür (yoksa npos). const auto noktalarOku = [&](size_t anahtar) -> size_t { const size_t ob = src.find('[', anahtar); if (ob == std::string::npos) return std::string::npos; const size_t cb = src.find(']', ob); if (cb == std::string::npos) return std::string::npos; pts.clear(); const char* p = src.c_str() + ob + 1; const char* son = src.c_str() + cb; double xyz[3]; int adet = 0; // from_chars (locale-bağımsız, strtod'dan ~5x hızlı): dev WRL'de asıl parse maliyeti. while (p < son) { while (p < son && *p != '-' && *p != '+' && *p != '.' && (*p < '0' || *p > '9')) ++p; if (p >= son) break; double v; const auto r = std::from_chars(p, son, v); if (r.ec != std::errc()) { ++p; continue; } p = r.ptr; xyz[adet++] = v; if (adet == 3) { pts.emplace_back(xyz[0] * sx, xyz[1] * sy, xyz[2] * sz); adet = 0; } } return cb + 1; }; // Kaynagi sirayla tara: IndexedFaceSet/IndexedLineSet mod belirler; point nokta // dizisini gunceller; coordIndex moda göre yüz (fan-triangule) ya da kenar (polyline // segment) üretir. En yakın anahtardan devam (pos ilerler → doğal Shape eşleşmesi). // PERF: her anahtar için "sonraki konum" ÖNBELLEĞİ (yoksa her iterasyon 5 anahtarı // baştan tarardı → tükenmiş anahtar 12MB'yi her seferinde sonuna dek tarar = O(n²), // 12MB dosyada ~17s). Yalnız pos bir anahtarı geçince O(1) yeniden aranır → O(n). // Web three.js VRMLLoader ile aynı lineer karmaşıklık. static constexpr const char* kAnahtar[5] = {"IndexedFaceSet", "IndexedLineSet", "point", "coordIndex", "material"}; size_t sonraki[5]; for (int i = 0; i < 5; ++i) sonraki[i] = kelimeBul(src, kAnahtar[i], 0); size_t pos = 0; while (pos < src.size()) { // pos geçtiğimiz (bayat) anahtarları yeniden ara; tükenmiş (npos) dokunulmaz. for (int i = 0; i < 5; ++i) if (sonraki[i] != std::string::npos && sonraki[i] < pos) sonraki[i] = kelimeBul(src, kAnahtar[i], pos); size_t en = std::string::npos; int tur = -1; // 0=Face,1=Line,2=point,3=coordIndex,4=material for (int i = 0; i < 5; ++i) if (sonraki[i] != std::string::npos && (en == std::string::npos || sonraki[i] < en)) { en = sonraki[i]; tur = i; } if (tur < 0) break; if (tur == 4) { // "material" (8) → DEF _ad Material {...diffuseColor} | USE _ad size_t q = en + 8; while (q < src.size() && std::isspace((unsigned char)src[q])) ++q; const auto adOku = [&](size_t& r) -> std::string { std::string ad; while (r < src.size() && !std::isspace((unsigned char)src[r]) && src[r] != '{') ad += src[r++]; return ad; }; if (src.compare(q, 3, "DEF") == 0) { q += 3; while (q < src.size() && std::isspace((unsigned char)src[q])) ++q; const std::string ad = adOku(q); const size_t dc = kelimeBul(src, "diffuseColor", q); if (dc != std::string::npos) { float r = 0.62f, g = 0.66f, b = 0.72f; std::sscanf(src.c_str() + dc + 12, " %f %f %f", &r, &g, &b); aktifRenk = {r, g, b}; if (!ad.empty()) matHaritasi[ad] = aktifRenk; pos = dc + 12; continue; } pos = q; } else if (src.compare(q, 3, "USE") == 0) { q += 3; while (q < src.size() && std::isspace((unsigned char)src[q])) ++q; const std::string ad = adOku(q); if (const auto it = matHaritasi.find(ad); it != matHaritasi.end()) aktifRenk = it->second; pos = q; } else { pos = en + 8; } } else if (tur == 0) { cizgiModu = false; ++aktifShape; pos = en + 14; } // "IndexedFaceSet" else if (tur == 1) { cizgiModu = true; pos = en + 14; } // "IndexedLineSet" (14) else if (tur == 2) { const size_t np = noktalarOku(en); if (np == std::string::npos) break; pos = np; } else { // coordIndex const size_t ob = src.find('[', en); if (ob == std::string::npos) break; const size_t cb = src.find(']', ob); if (cb == std::string::npos) break; const char* p = src.c_str() + ob + 1; const char* son = src.c_str() + cb; std::vector dizi; dizi.reserve(64); // PERF: Shape başına TEK triangulation-face (üçgen başına DEĞİL). Eskiden her // üçgen ayrı TopoDS_Face'ti → dev WRL'de 150k+ yüz → BVH/hover/pick kasması. // Şimdi bir IndexedFaceSet'in TÜM üçgenleri tek yüzün Poly_Triangulation'ında // → yüz sayısı = Shape sayısı (CATIA yüzleri, ~40x az). Node = pts (bu Shape). std::vector ucgenNode; // düzleştirilmiş a,b,c (0-tabanlı, pts indeksi) ucgenNode.reserve(pts.size() * 3); // ~her düğüm bir üçgende → tahsis çakılmaz const auto yuzuIsle = [&]() { // fan-triangule → birikime ekle for (size_t k = 1; k + 1 < dizi.size(); ++k) { const int a = dizi[0], b = dizi[k], c = dizi[k + 1]; if (a < 0 || b < 0 || c < 0 || (size_t)a >= pts.size() || (size_t)b >= pts.size() || (size_t)c >= pts.size()) continue; ucgenNode.push_back(a); ucgenNode.push_back(b); ucgenNode.push_back(c); } dizi.clear(); }; const auto cizgiIsle = [&]() { // polyline → görsel segmentler + GERÇEK BREP kenarı if (dizi.size() >= 2) { ++kenarOrdinal; // Görsel çizgi listesi (GPU kenar pick ordinalı = kenarOrdinal). if (kenarlarCikti) for (size_t k = 0; k + 1 < dizi.size(); ++k) { const int a = dizi[k], b = dizi[k + 1]; if (a < 0 || b < 0 || (size_t)a >= pts.size() || (size_t)b >= pts.size()) continue; const gp_Pnt& pa = pts[a]; const gp_Pnt& pb = pts[b]; kenarlarCikti->vertexler.push_back( {{(f32)pa.X(), (f32)pa.Y(), (f32)pa.Z()}, kenarOrdinal}); kenarlarCikti->vertexler.push_back( {{(f32)pb.X(), (f32)pb.Y(), (f32)pb.Z()}, kenarOrdinal}); } // BREP kenarı: polyline → TopoDS_Edge (kenarBilgi/ölçüm/hover STEP paritesi). // 2 nokta → doğru kenar (dogruMu çalışır); çok nokta → degree-1 BSpline // (poligon eğri — kord-boyu knot). ORDİNAL HİZASI KRİTİK: her line-set tam // bir kenar üretmeli (görsel kenarOrdinal ↔ TopExp EDGE sırası birebir) → // dejenere sette bile minik yedek kenar eklenir, sayaç asla kaymaz. std::vector zincir; zincir.reserve(dizi.size()); for (int idx : dizi) { if (idx < 0 || (size_t)idx >= pts.size()) continue; const gp_Pnt& p = pts[(size_t)idx]; if (!zincir.empty() && zincir.back().SquareDistance(p) < 1e-14) continue; zincir.push_back(p); } TopoDS_Edge kenarSekil; try { if (zincir.size() == 2) { kenarSekil = BRepBuilderAPI_MakeEdge(zincir[0], zincir[1]); } else if (zincir.size() > 2) { const int n = (int)zincir.size(); TColgp_Array1OfPnt kutup(1, n); TColStd_Array1OfReal dugum(1, n); TColStd_Array1OfInteger kat(1, n); f64 t = 0; for (int i = 1; i <= n; ++i) { kutup.SetValue(i, zincir[(size_t)i - 1]); if (i > 1) t += zincir[(size_t)i - 1].Distance(zincir[(size_t)i - 2]); dugum.SetValue(i, t); kat.SetValue(i, (i == 1 || i == n) ? 2 : 1); } Handle(Geom_BSplineCurve) egri = new Geom_BSplineCurve(kutup, dugum, kat, 1); kenarSekil = BRepBuilderAPI_MakeEdge(egri); } } catch (const Standard_Failure&) { kenarSekil.Nullify(); } if (kenarSekil.IsNull()) { // Yedek: hiza korunsun diye minik doğru sentinel (pratikte oluşmaz). try { const gp_Pnt p0 = zincir.empty() ? gp_Pnt(0, 0, 0) : zincir.front(); kenarSekil = BRepBuilderAPI_MakeEdge(p0, gp_Pnt(p0.X() + 1e-6, p0.Y(), p0.Z())); } catch (const Standard_Failure&) { } } if (!kenarSekil.IsNull()) insaci.Add(comp, kenarSekil); } dizi.clear(); }; // from_chars (int): 468k+ coordIndex değeri → strtol'den çok hızlı, locale-bağımsız. while (p < son) { while (p < son && *p != '-' && (*p < '0' || *p > '9')) ++p; if (p >= son) break; int v; const auto r = std::from_chars(p, son, v); if (r.ec != std::errc()) { ++p; continue; } p = r.ptr; if (v == -1) { if (cizgiModu) cizgiIsle(); else yuzuIsle(); } else dizi.push_back(v); } if (!dizi.empty()) { if (cizgiModu) cizgiIsle(); else yuzuIsle(); } // FACE modu → biriken üçgenlerden TEK triangulation-face (bu Shape). if (!cizgiModu && !ucgenNode.empty()) { try { const int nt = (int)(ucgenNode.size() / 3); Handle(Poly_Triangulation) tri = new Poly_Triangulation((int)pts.size(), nt, Standard_False); for (int i = 0; i < (int)pts.size(); ++i) tri->SetNode(i + 1, pts[(size_t)i]); for (int i = 0; i < nt; ++i) tri->SetTriangle(i + 1, Poly_Triangle(ucgenNode[(size_t)i * 3] + 1, ucgenNode[(size_t)i * 3 + 1] + 1, ucgenNode[(size_t)i * 3 + 2] + 1)); TopoDS_Face yuz; insaci.MakeFace(yuz); // yüzeysiz yüz (yalnız triangulation) insaci.UpdateFace(yuz, tri); // triangulation'ı bağla insaci.Add(comp, yuz); ++yuzSayi; if (renklerCikti) renklerCikti->push_back(aktifRenk); // yüz-ordinal sırası if (yuzShapeCikti) yuzShapeCikti->push_back(aktifShape); // yüz → Shape (artık 1:1) } catch (const Standard_Failure&) { } } pos = cb + 1; } } if (kenarlarCikti) kenarlarCikti->kenarSayi = kenarOrdinal; gunluk::bilgi("VRML yuklendi: {} yuz, {} kenar-grubu ({})", yuzSayi, kenarOrdinal, yol); return ic::sar(comp); } catch (const Standard_Failure& hata) { gunluk::hata("VRML okuma hatasi: {}", hata.what()); return {}; } } // GECICI: per-face XCAF renk destekli STEPCAFControl_Writer surumu commit edilmeden // yarim birakilmisti ve okunamayan STEP uretiyordu (bkz. brep_io.cpp diagnostigi, // 2026-08-07) — STL/IGES/OBJ export'lari calisirken STEP round-trip testi cokuyordu. // Bilinen-calisan basit yazicaya donduruldu; per-face renk export'u AYRI bir // gorev olarak (STEPCAFControl_Writer + AddShape/SetColor akisi ADIM ADIM // dogrulanarak) yeniden eklenmeli. bool stepKaydet(const std::vector& govdeler, const std::string& yol) { try { STEPControl_Writer yazici; for (const BrepGovde* g : govdeler) if (g && g->gecerliMi()) yazici.Transfer(sekil(*g), STEPControl_AsIs); const bool tamam = yazici.Write(yol.c_str()) == IFSelect_RetDone; if (tamam) gunluk::bilgi("STEP kaydedildi: {}", yol); return tamam; } catch (const Standard_Failure& hata) { gunluk::hata("STEP kaydetme hatasi: {}", hata.what()); return false; } } bool stlKaydet(const std::vector& govdeler, const std::string& yol, bool ikili, double sapma) { try { // Tüm gövdeleri tek bileşiğe topla → tek dosyada çok-gövde STL. TopoDS_Compound bilesik; BRep_Builder insaci; insaci.MakeCompound(bilesik); int sayi = 0; for (const BrepGovde* g : govdeler) if (g && g->gecerliMi()) { insaci.Add(bilesik, sekil(*g)); ++sayi; } if (sayi == 0) { gunluk::hata("STL: kaydedilecek gecerli govde yok"); return false; } // STL üçgen mesh ister → BRepMesh ile sapma toleransında üçgenleştir (paralel). BRepMesh_IncrementalMesh(bilesik, sapma > 0.0 ? sapma : 0.1, false, 0.5, true); StlAPI_Writer yazici; yazici.ASCIIMode() = !ikili; // ikili=true → binary STL (kompakt), false → ASCII (okunur) const bool tamam = yazici.Write(bilesik, yol.c_str()); if (tamam) gunluk::bilgi("STL kaydedildi ({}, {} govde): {}", ikili ? "ikili" : "ascii", sayi, yol); else gunluk::hata("STL yazma basarisiz: {}", yol); return tamam; } catch (const Standard_Failure& hata) { gunluk::hata("STL kaydetme hatasi: {}", hata.what()); return false; } } bool igesKaydet(const std::vector& govdeler, const std::string& yol) { try { IGESControl_Controller::Init(); // IGES kullanımından önce controller kaydı (bir kez yeter) IGESControl_Writer yazici; int sayi = 0; for (const BrepGovde* g : govdeler) if (g && g->gecerliMi()) { yazici.AddShape(sekil(*g)); ++sayi; } if (sayi == 0) { gunluk::hata("IGES: kaydedilecek gecerli govde yok"); return false; } yazici.ComputeModel(); const bool tamam = yazici.Write(yol.c_str()); if (tamam) gunluk::bilgi("IGES kaydedildi ({} govde): {}", sayi, yol); else gunluk::hata("IGES yazma basarisiz: {}", yol); return tamam; } catch (const Standard_Failure& hata) { gunluk::hata("IGES kaydetme hatasi: {}", hata.what()); return false; } } bool objKaydet(const std::vector& govdeler, const std::string& yol, double kalite) { try { std::ofstream f(yol, std::ios::binary); if (!f) { gunluk::hata("OBJ: dosya acilamadi: {}", yol); return false; } f << "# FikoYT OBJ export\n"; size_t vTaban = 1; // OBJ 1-indeksli (vertex numaraları 1'den başlar) int sayi = 0; for (const BrepGovde* g : govdeler) { if (!g || !g->gecerliMi()) continue; const MeshVeri m = g->ucgenle({0.7f, 0.7f, 0.7f}, kalite > 0.0 ? kalite : 0.004); if (m.vertexler.empty()) continue; f << "o govde_" << (sayi + 1) << "\n"; for (const auto& v : m.vertexler) f << "v " << v.konum[0] << ' ' << v.konum[1] << ' ' << v.konum[2] << "\n"; for (size_t i = 0; i + 2 < m.indeksler.size(); i += 3) f << "f " << (vTaban + m.indeksler[i]) << ' ' << (vTaban + m.indeksler[i + 1]) << ' ' << (vTaban + m.indeksler[i + 2]) << "\n"; vTaban += m.vertexler.size(); ++sayi; } if (sayi == 0) { gunluk::hata("OBJ: kaydedilecek gecerli govde yok"); return false; } gunluk::bilgi("OBJ kaydedildi ({} govde): {}", sayi, yol); return true; } catch (const std::exception& hata) { gunluk::hata("OBJ kaydetme hatasi: {}", hata.what()); return false; } } } // namespace fiko::geometri