#include "fiko/cam/cam_op_hesap.hpp" #include #include #include #include "fiko/geometri/poligon_ofset.hpp" namespace fiko::cam { namespace { using geometri::Poligon; using geometri::PoligonKumesi; using geometri::poligonFark; using geometri::poligonOfset; // ---- Makro dizisinden yardimcilar (linking) -------------------------------- // Op makrolarindan verilen tipteki (aktif) makroyu bul; yoksa nullptr. const CamMakro* makroBul(const CamOperasyon& op, MakroTip tip) { for (const CamMakro& m : op.makrolar) if (m.aktif && m.tip == tip) return &m; return nullptr; } // Clearance (guvenlik) Z: Clearance makrosu varsa ondan, yoksa geometri ustunun // makul ustu. Yaklasma (feed-plane) Z: Approach makrosundan. f64 clearanceZ(const CamOperasyon& op) { if (const CamMakro* c = makroBul(op, MakroTip::Clearance)) return c->guvenlikZ; if (const CamMakro* a = makroBul(op, MakroTip::Approach)) return a->guvenlikZ; return op.geometri.ustZ + 25.0; } f64 yaklasmaZ(const CamOperasyon& op) { if (const CamMakro* a = makroBul(op, MakroTip::Approach)) return a->yaklasmaZ; return op.geometri.ustZ + 5.0; } // Approach makro hareketini mevcut CamIslem giris tipine cevir (rampa/helis/dikine). GirisTip girisTipiSec(const CamOperasyon& op) { const CamMakro* a = makroBul(op, MakroTip::Approach); const MakroHareket h = a ? a->hareketTip : MakroHareket::Rampa; switch (h) { case MakroHareket::Helis: return GirisTip::Helis; case MakroHareket::Dikine: case MakroHareket::OnDelik: return GirisTip::Dalma; default: return GirisTip::Rampa; // Rampa/Yay/TegetCizgi → rampa yaklasimi } } // ---- CamOperasyon → CamIslem shim (2.5D motoru yeniden kullan) ------------- // Ortak Z/hiz/makro alanlarini eski 2.5D CamIslem'e aktarir. Kontur/geometri // cagiran tarafta ayrica verilir (freze icin konturlar[], delik icin merkezler[]). CamIslem shimYap(const CamOperasyon& op) { CamIslem p; p.kimlik = op.kimlik; p.ad = op.ad; // Yukseklikler (dunya Z). p.ustZ = op.geometri.ustZ; p.altZ = op.geometri.altZ - (op.geometri.tabanAcik ? op.geometri.breakthrough : 0.0); p.guvenliZ = clearanceZ(op); p.geriCekmeZ = yaklasmaZ(op); // Gecisler. p.zHatve = std::max(0.05, op.strateji.eksenelHatve); p.yuzeyPayi = std::max(0.0, op.strateji.finisPayi); // Baglanti / giris. p.giris = girisTipiSec(op); if (const CamMakro* a = makroBul(op, MakroTip::Approach)) { p.rampaAci = a->aci > 0.1 ? a->aci : 5.0; if (a->yaricap > 0.1) p.helisCapOran = 0.6; // helis capi shim: takim capiyla oranli } // Hizlar. p.devir = op.hizlar.devir; p.ilerleme = op.hizlar.ilerleme; p.dalmaIlerleme = op.hizlar.dalmaIlerleme; // Tab / kopru (contour son paso). p.tabEtkin = op.strateji.tabAktif; p.tabSayi = std::max(1, op.strateji.tabAdet); p.tabGenislik = op.strateji.tabGenislik; p.tabYukseklik = op.strateji.tabYukseklik; return p; } void hamle(TakimYolu& y, HamleTip tip, f64 x, f64 yy, f64 z, f64 f = 0) { y.hamleler.push_back({tip, {x, yy, z}, f}); } // ---- Facing: kontur icine kirpilmis aci-yonlu zigzag tarama ---------------- // Konturu takim yaricapi kadar iceri ofsetle (islenebilir bolge), sinir kutusu // uzerinde rasterAci yonunde taramalar uret, kontur icindeki parcalari kes. // Kontur-cizgi kesisimi: her tarama cizgisini kontur kenarlariyla kesip // [giris,cikis] araligini kes. Basit ama dogru (konveks/konkav dolu bolgede). struct Aralik { f64 t0, t1; }; // Nokta poligon icinde mi (ray-casting). bool icinde(const Poligon& poli, f64 x, f64 y) { bool ic = false; const size_t n = poli.size(); for (size_t i = 0, j = n - 1; i < n; j = i++) { const Vec2d& a = poli[i]; const Vec2d& b = poli[j]; if (((a.y > y) != (b.y > y)) && (x < (b.x - a.x) * (y - a.y) / (b.y - a.y + 1e-30) + a.x)) ic = !ic; } return ic; } TakimYolu facingYolu(const CamOperasyon& op, const Takim& t, const CamIslem& p) { TakimYolu yol; if (op.geometri.konturlar.empty() || op.geometri.konturlar[0].size() < 3) return yol; const f64 r = t.cap * 0.5; // Islenebilir bolge: dis konturu iceri, adalari disari ofsetle (delme). PoligonKumesi bolge = poligonOfset({op.geometri.konturlar[0]}, -(r + p.yuzeyPayi)); if (!op.geometri.adalar.empty()) { const PoligonKumesi ad = poligonOfset(op.geometri.adalar, r + p.yuzeyPayi); if (!ad.empty()) bolge = poligonFark(bolge, ad); } if (bolge.empty()) return yol; // Sinir kutusu. f64 x0 = 1e300, y0 = 1e300, x1 = -1e300, y1 = -1e300; for (const Poligon& poli : bolge) for (const Vec2d& v : poli) { x0 = std::min(x0, v.x); y0 = std::min(y0, v.y); x1 = std::max(x1, v.x); y1 = std::max(y1, v.y); } const f64 aci = op.strateji.rasterAci * std::numbers::pi / 180.0; const Vec2d u{std::cos(aci), std::sin(aci)}; // tarama yonu const Vec2d v{-u.y, u.x}; // adim yonu const Vec2d merkez{(x0 + x1) * 0.5, (y0 + y1) * 0.5}; const f64 yariKosegen = 0.5 * std::hypot(x1 - x0, y1 - y0) + r; const f64 hatve = std::max(0.2, op.strateji.yanalHatveYuzde * 0.01 * t.cap); const f64 z = p.altZ; // Facing tek seviye (yuzey tarama) — altZ'de. zPasolari gerekmez. // Tarama cizgisi ornekleme adimi: ~0.5mm (kontur ici/dis gecislerini yakalar). const int N = std::max(8, (int)std::ceil(2.0 * yariKosegen / 0.5)); bool ileri = true, ilkPaso = true; Vec2d sonUc{}; for (f64 sk = -yariKosegen; sk <= yariKosegen + 1e-9; sk += hatve, ileri = !ileri) { const Vec2d o = merkez + v * sk; // Bu tarama cizgisi uzerinde kontur ici parcalari topla. std::vector araliklar; bool ictePrev = false; f64 girisT = 0; for (int i = 0; i <= N; ++i) { const f64 tParam = -yariKosegen + (2.0 * yariKosegen) * (f64)i / (f64)N; const Vec2d q = o + u * tParam; bool ic = false; for (const Poligon& poli : bolge) if (icinde(poli, q.x, q.y)) { ic = true; break; } if (ic && !ictePrev) girisT = tParam; if (!ic && ictePrev) araliklar.push_back({girisT, tParam}); ictePrev = ic; } if (ictePrev) araliklar.push_back({girisT, yariKosegen}); for (Aralik& a : araliklar) { Vec2d bas = o + u * a.t0, son = o + u * a.t1; if (!ileri) std::swap(bas, son); if (ilkPaso) { hamle(yol, HamleTip::Hizli, bas.x, bas.y, p.guvenliZ); hamle(yol, HamleTip::Dalma, bas.x, bas.y, z, p.dalmaIlerleme); ilkPaso = false; } else { // Kisa yatay baglanti (stay-down) mesafeye gore: yakinsa kesme, uzaksa geri cek. const f64 bagUz = (bas - sonUc).uzunluk(); if (bagUz > 2.5 * hatve) { hamle(yol, HamleTip::Hizli, sonUc.x, sonUc.y, p.guvenliZ); hamle(yol, HamleTip::Hizli, bas.x, bas.y, p.guvenliZ); hamle(yol, HamleTip::Dalma, bas.x, bas.y, z, p.dalmaIlerleme); } else { hamle(yol, HamleTip::Kesme, bas.x, bas.y, z, p.ilerleme); } } hamle(yol, HamleTip::Kesme, son.x, son.y, z, p.ilerleme); sonUc = son; } } if (!ilkPaso) hamle(yol, HamleTip::Hizli, sonUc.x, sonUc.y, p.guvenliZ); yol.istatistikGuncelle(); return yol; } // ---- CircularMill: merkez etrafinda es-stepover dairesel pasolar ------------ // geometri.baslangic = daire merkezi (dunya XY); geometri.cap = hedef cap. // strateji.daireYolSayisi/daireYolMesafe radyal pas kontrolu. TakimYolu circularMillYolu(const CamOperasyon& op, const Takim& t, const CamIslem& p) { TakimYolu yol; const f64 hedefR = op.geometri.cap * 0.5; const f64 takimR = t.cap * 0.5; if (hedefR <= takimR + 1e-6) return yol; // takim delige sigmiyor const Vec2d merkez = op.geometri.baslangic; const f64 disR = hedefR - takimR - p.yuzeyPayi; // takim merkez yaricapi const int paso = std::max(1, op.strateji.daireYolSayisi); const f64 pasoMesafe = std::max(0.2, op.strateji.daireYolMesafe); const int seg = 48; // Z pasolari (eksenel). std::vector zs; { const f64 adim = std::max(0.05, p.zHatve); f64 z = p.ustZ; while (z > p.altZ + 1e-9) { z = std::max(p.altZ, z - adim); zs.push_back(z); } if (zs.empty()) zs.push_back(p.altZ); } for (const f64 z : zs) { for (int k = 0; k < paso; ++k) { const f64 rr = std::max(0.0, disR - pasoMesafe * (f64)(paso - 1 - k)); const Vec2d bas{merkez.x + rr, merkez.y}; hamle(yol, HamleTip::Hizli, bas.x, bas.y, p.guvenliZ); hamle(yol, HamleTip::Dalma, bas.x, bas.y, z, p.dalmaIlerleme); for (int i = 1; i <= seg; ++i) { const f64 a = 2.0 * std::numbers::pi * (f64)i / (f64)seg; hamle(yol, HamleTip::Kesme, merkez.x + rr * std::cos(a), merkez.y + rr * std::sin(a), z, p.ilerleme); } hamle(yol, HamleTip::Hizli, bas.x, bas.y, p.guvenliZ); } } yol.istatistikGuncelle(); return yol; } // ---- Axial (delme ailesi): CamGeometri.noktalar → delikYolu shim ----------- DelikDongu axialDonguSec(const CamOperasyon& op) { switch (op.tip) { case CamIslemTipCatia::SpotDrill: return DelikDongu::Punta; case CamIslemTipCatia::Tapping: case CamIslemTipCatia::ReverseTap: return DelikDongu::Kilavuz; case CamIslemTipCatia::Reaming: return DelikDongu::Rayba; case CamIslemTipCatia::CounterBore: case CamIslemTipCatia::CounterSink: case CamIslemTipCatia::BoringChamfer: return DelikDongu::Havsa; default: break; } // DeepHole/BreakChips veya strateji.dongu gagali/talas → peck. if (op.tip == CamIslemTipCatia::DeepHole || op.tip == CamIslemTipCatia::BreakChips || op.strateji.dongu == AxialDongu::Gagali || op.strateji.dongu == AxialDongu::TalasKir) return DelikDongu::PeckDelme; return DelikDongu::Delme; } TakimYolu axialYolu(const CamOperasyon& op, const CamIslem& shim) { // Aktif noktalari topla; sira: NoktaSira::EnYakin → delikYolu zaten en-yakin // komsu siralar. Secim sirasi isteniyorsa noktalari oldugu gibi verip // delikYolu'nun siralamasina birakiriz (mevcut motor en-yakin). Secim sirasi // icin baslangic noktasindan basla; en-yakin motorunu kullan (kisa yol). std::vector merkezler; const f64 ustZ = op.geometri.ustZ; // Baslangic isaretli nokta varsa listenin basina al (delikYolu ilk noktadan gezer). int basIdx = -1; for (size_t i = 0; i < op.geometri.noktalar.size(); ++i) { const CamNokta& n = op.geometri.noktalar[i]; if (!n.aktif) continue; if (n.baslangic && basIdx < 0) basIdx = (int)merkezler.size(); merkezler.push_back({n.x, n.y, ustZ}); } if (op.strateji.noktaSirala == NoktaSira::Secim && basIdx > 0 && basIdx < (int)merkezler.size()) std::swap(merkezler[0], merkezler[(size_t)basIdx]); CamIslem p = shim; p.dongu = axialDonguSec(op); p.peckDerinlik = std::max(0.1, op.strateji.peckDerinlik); p.geriCekmeZ = yaklasmaZ(op); p.disAdimi = 1.0; // Axial derinlik: altZ = ustZ - derinlik (geometri.derinlik alani). p.altZ = op.geometri.altZ; if (op.geometri.derinlik > 1e-6) p.altZ = ustZ - op.geometri.derinlik; return delikYolu(p, merkezler); } // ---- Linking makro sarici (giris/cikis clearance) -------------------------- // Yol ilk/son hamlesini clearance Z'de rapid ile cevrele (Clearance makrosu). // Alt motorlar zaten guvenliZ rapidleriyle basliyor/bitiyor; burada eksikse // tamamlariz (simetrik giris/cikis garantisi). NaN Z guard. inline bool noktaSonlu(const Vec3d& p) { return std::isfinite(p.x) && std::isfinite(p.y) && std::isfinite(p.z); } void makroylaCevrele(TakimYolu& yol, const CamOperasyon& op) { if (yol.hamleler.empty()) return; const f64 cz = clearanceZ(op); if (!std::isfinite(cz)) return; const Vec3d& ilk = yol.hamleler.front().nokta; const Vec3d& son = yol.hamleler.back().nokta; // Bas: ilk hamle clearance'ta rapid degilse ekle (ilk XY sonlu olmali). if (noktaSonlu(ilk) && !(yol.hamleler.front().tip == HamleTip::Hizli && ilk.z >= cz - 1e-6)) { Hamle bas{HamleTip::Hizli, {ilk.x, ilk.y, cz}, 0}; if (noktaSonlu(bas.nokta)) yol.hamleler.insert(yol.hamleler.begin(), bas); } // Son: clearance'a geri cek. if (noktaSonlu(son) && !(yol.hamleler.back().tip == HamleTip::Hizli && son.z >= cz - 1e-6)) { hamle(yol, HamleTip::Hizli, son.x, son.y, cz); } yol.istatistikGuncelle(); } } // namespace TakimYolu opYoluHesapla(const CamOperasyon& op, const Takim& t, const HesapGeri& geri, std::string& hata) { hata.clear(); TakimYolu yol; // Ortak geometri gecerlilik kontrolu (Turkce hata, cokme yok). const bool axial = axialMi(op.tip); if (axial) { bool nokta = false; for (const CamNokta& n : op.geometri.noktalar) if (n.aktif) { nokta = true; break; } if (!nokta) { hata = "Delik noktası seçilmedi (Geometri → nokta seçin)"; return yol; } } else if (op.tip != CamIslemTipCatia::CircularMill) { if (op.geometri.konturlar.empty() || op.geometri.konturlar[0].size() < 3) { hata = "Kontur seçilmedi veya geçersiz (en az 3 nokta gerekli)"; return yol; } } if (op.geometri.altZ >= op.geometri.ustZ - 1e-6 && !axial) { hata = "İşleme derinliği geçersiz (alt Z ≥ üst Z)"; return yol; } const CamIslem shim = shimYap(op); // TakimYonu.yon != Z → yol yerel düzlemde üretilir (G68.2 post sonraki iş). // Şimdilik hata değil; not olarak sessiz geçilir (yön XY yola işlenmez). switch (op.tip) { case CamIslemTipCatia::Facing: yol = facingYolu(op, t, shim); break; case CamIslemTipCatia::Pocketing: { // Kontur + adalar → cepYolu (mevcut motor: exact offset, ada koruma). std::vector ks; ks.push_back(op.geometri.konturlar[0]); for (const auto& ad : op.geometri.adalar) if (ad.size() >= 3) ks.push_back(ad); yol = cepYolu(shim, t, ks, geri); break; } case CamIslemTipCatia::Contouring: case CamIslemTipCatia::CurveFollow: { // Taraf (Icerde/Disarda/Uzerinde) → OfsetYonu. CamIslem p = shim; switch (op.strateji.taraf) { case TakimTarafi::Disarda: p.ofsetYon = OfsetYonu::Sol; break; // dış telafi case TakimTarafi::Icerde: p.ofsetYon = OfsetYonu::Sag; break; // iç telafi default: p.ofsetYon = OfsetYonu::Merkez; break; } std::vector ks{op.geometri.konturlar[0]}; yol = profilYolu(p, t, ks); break; } case CamIslemTipCatia::CircularMill: case CamIslemTipCatia::TSlotting: yol = circularMillYolu(op, t, shim); if (yol.bosMu() && hata.empty()) hata = "Dairesel yol üretilemedi (çap takıma göre küçük olabilir)"; break; default: if (axial) { yol = axialYolu(op, shim); } else { // PointToPoint vb. → profil gibi tek kontur işle (güvenli varsayılan). std::vector ks{op.geometri.konturlar[0]}; yol = profilYolu(shim, t, ks); } break; } if (yol.bosMu()) { if (hata.empty()) hata = "Takım yolu üretilemedi (takım bu geometriye sığmıyor olabilir)"; return yol; } // Linking makrolarını uygula (giriş/çıkış clearance çerçevesi). makroylaCevrele(yol, op); // K6: NaN/Inf hamle koordinatı içeren yolu reddet (fit/simülasyona taşımaz). for (const Hamle& h : yol.hamleler) { if (!noktaSonlu(h.nokta)) { hata = "Takım yolu geçersiz koordinat içeriyor (NaN); yol reddedildi"; yol.hamleler.clear(); yol.istatistikGuncelle(); return yol; } } return yol; } } // namespace fiko::cam