#include "fiko/cam/cam_hesap.hpp" #include #include #include #include #include "fiko/geometri/poligon_ofset.hpp" // OpenCAMLib (vendor: ucuncu_taraf/opencamlib) #include "adaptivepathdropcutter.hpp" #include "adaptivewaterline.hpp" #include "ballcutter.hpp" #include "batchdropcutter.hpp" #include "bullcutter.hpp" #include "ccpoint.hpp" #include "clpoint.hpp" #include "conecutter.hpp" #include "cylcutter.hpp" #include "line.hpp" #include "lineclfilter.hpp" #include "path.hpp" #include "point.hpp" #include "stlsurf.hpp" #include "triangle.hpp" #include "waterline.hpp" namespace fiko::cam { namespace { using geometri::Poligon; using geometri::PoligonKumesi; using geometri::poligonBirlesim; using geometri::poligonFark; using geometri::poligonOfset; // Kontur alt-kumesi (bas..son) → PoligonKumesi (>=3 nokta). PoligonKumesi altKume(const std::vector& ks, size_t bas, size_t son) { PoligonKumesi p; for (size_t i = bas; i < son && i < ks.size(); ++i) if (ks[i].size() >= 3) p.push_back(ks[i]); 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}); } // Z paso listesi: ustZ'den altZ'ye zHatve adimlariyla (son paso tam altZ). std::vector zPasolari(const CamIslem& p) { 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); return zs; } struct KapaliYol { std::vector n; // kapali (son != ilk) std::vector s; // kumulatif yay uzunlugu (n.size()+1; son = cevre) f64 cevre = 0; explicit KapaliYol(const Poligon& p) { n = p; s.resize(n.size() + 1, 0); for (size_t i = 0; i < n.size(); ++i) s[i + 1] = s[i] + (n[(i + 1) % n.size()] - n[i]).uzunluk(); cevre = s[n.size()]; } }; // Tab penceresi: cevre uzerinde esit dagitilmis tabSayi adet, tabGenislik genisliginde. bool tabIcinde(const CamIslem& p, f64 cevre, f64 sk) { if (!p.tabEtkin || p.tabSayi <= 0 || cevre <= 0) return false; const f64 aralik = cevre / (f64)p.tabSayi; const f64 yari = p.tabGenislik * 0.5; for (int i = 0; i < p.tabSayi; ++i) { const f64 merkez = aralik * ((f64)i + 0.5); f64 fark = std::abs(sk - merkez); fark = std::min(fark, cevre - fark); if (fark <= yari) return true; } return false; } // Kapali konturu z pasosunda kes (baslangic → tam tur → baslangic). // Tab etkin ve paso tab ustunun altindaysa pencere icinde tabUst'e kaldirilir. void konturKes(TakimYolu& yol, const CamIslem& p, const KapaliYol& k, f64 z, bool tabAktif) { const f64 tabUst = p.altZ + p.tabYukseklik; const bool tabli = tabAktif && p.tabEtkin && z < tabUst - 1e-9; const size_t n = k.n.size(); for (size_t i = 0; i < n; ++i) { const Vec2d a = k.n[i], b = k.n[(i + 1) % n]; if (!tabli) { hamle(yol, HamleTip::Kesme, b.x, b.y, z, p.ilerleme); continue; } // Tab pasosu: segmenti ~1mm adimlarla bol, pencere icinde Z kaldir. const f64 uz = (b - a).uzunluk(); const int adet = std::max(1, (int)std::ceil(uz / 1.0)); for (int j = 1; j <= adet; ++j) { const f64 t = (f64)j / (f64)adet; const Vec2d q = a + (b - a) * t; const f64 sk = std::fmod(k.s[i] + uz * t, std::max(k.cevre, 1e-9)); const f64 zq = tabIcinde(p, k.cevre, sk) ? tabUst : z; hamle(yol, HamleTip::Kesme, q.x, q.y, zq, p.ilerleme); } } } // Kontur baslangicina giris: Dalma duz iner; Rampa kontur boyunca acili iner; // Helis baslangic noktasinda dairesel helisle iner. oncekiZ'den z'ye. void girisYap(TakimYolu& yol, const CamIslem& p, const Takim& t, const KapaliYol& k, f64 oncekiZ, f64 z) { const Vec2d bas = k.n[0]; switch (p.giris) { case GirisTip::Dalma: hamle(yol, HamleTip::Dalma, bas.x, bas.y, z, p.dalmaIlerleme); return; case GirisTip::Rampa: { const f64 egim = std::tan(std::max(0.5, p.rampaAci) * std::numbers::pi / 180.0); f64 zg = oncekiZ; size_t i = 0; int emniyet = 0; while (zg > z + 1e-9 && emniyet < 100000) { const Vec2d a = k.n[i % k.n.size()], b = k.n[(i + 1) % k.n.size()]; const f64 uz = (b - a).uzunluk(); zg = std::max(z, zg - uz * egim); hamle(yol, HamleTip::Dalma, b.x, b.y, zg, p.dalmaIlerleme); ++i; ++emniyet; } // Rampa kontur ortasinda bitti → baslangica kesmeyle tamamla (tam tur asagida). if (i % k.n.size() != 0) for (size_t j = i % k.n.size(); j <= k.n.size(); ++j) { const Vec2d q = k.n[j % k.n.size()]; hamle(yol, HamleTip::Kesme, q.x, q.y, z, p.ilerleme); } return; } case GirisTip::Helis: { const f64 rH = std::max(0.5, p.helisCapOran * t.cap * 0.5); const f64 egim = std::tan(std::max(0.5, p.rampaAci) * std::numbers::pi / 180.0); const f64 tur = 2.0 * std::numbers::pi * rH * egim; // tur basina inis const int turSay = std::max(1, (int)std::ceil((oncekiZ - z) / std::max(tur, 0.1))); const int seg = 24; const Vec2d mrk{bas.x - rH, bas.y}; // helis merkezi for (int i = 1; i <= turSay * seg; ++i) { const f64 aci = 2.0 * std::numbers::pi * (f64)i / (f64)seg; const f64 zg = std::max(z, oncekiZ - (oncekiZ - z) * (f64)i / (f64)(turSay * seg)); hamle(yol, HamleTip::Dalma, mrk.x + rH * std::cos(aci), mrk.y + rH * std::sin(aci), zg, p.dalmaIlerleme); } hamle(yol, HamleTip::Kesme, bas.x, bas.y, z, p.ilerleme); // merkeze donus return; } } } // ---- OpenCAMLib koprusu ---- std::unique_ptr kesiciOlustur(const Takim& t) { const f64 boy = std::max(10.0, t.boy); switch (t.tip) { case TakimTip::KureselFreze: return std::make_unique(t.cap, boy); case TakimTip::TorusFreze: return std::make_unique(t.cap, std::max(0.1, t.koseYaricap), boy); case TakimTip::KonikFreze: return std::make_unique( t.cap, std::max(5.0, t.ucAci * 0.5) * std::numbers::pi / 180.0, boy); default: return std::make_unique(t.cap, boy); } } void meshYukle(ocl::STLSurf& surf, const MeshGirdi& mesh) { for (size_t i = 0; i + 2 < mesh.indeksler.size(); i += 3) { const Vec3d& a = mesh.noktalar[mesh.indeksler[i]]; const Vec3d& b = mesh.noktalar[mesh.indeksler[i + 1]]; const Vec3d& c = mesh.noktalar[mesh.indeksler[i + 2]]; // Dejenere ucgenleri atla (sifir alan → ocl sayisal sorunlari) if ((b - a).capraz(c - a).uzunlukKare() < 1e-16) continue; surf.addTriangle(ocl::Triangle(ocl::Point(a.x, a.y, a.z), ocl::Point(b.x, b.y, b.z), ocl::Point(c.x, c.y, c.z))); } } // Mesh XY siniri (r kadar genisletilmis). Bos mesh → gecersiz (x1 x0 && y1 > y0; } f64 gen() const { return x1 - x0; } f64 der() const { return y1 - y0; } }; XYSinir xySinir(const MeshGirdi& mesh, f64 pay) { XYSinir s; for (const Vec3d& v : mesh.noktalar) { s.x0 = std::min(s.x0, v.x); s.y0 = std::min(s.y0, v.y); s.x1 = std::max(s.x1, v.x); s.y1 = std::max(s.y1, v.y); } s.x0 -= pay; s.y0 -= pay; s.x1 += pay; s.y1 += pay; return s; } // Scallop yuksekligi h → yanal hatve. Kuresel/torus icin geometrik bagintidan: // stepover = 2*sqrt(2*R*h - h^2). R = etkin uc yaricapi. h yoksa oransal hatveye don. f64 scallopHatve(const CamIslem& p, const Takim& t) { const f64 oransal = std::max(0.05, p.yanalHatveOran * t.cap); if (p.scallop <= 1e-6) return oransal; f64 R = t.cap * 0.5; if (t.tip == TakimTip::TorusFreze) R = std::max(0.1, t.koseYaricap); const f64 h = std::min(p.scallop, R * 0.98); const f64 so = 2.0 * std::sqrt(std::max(1e-9, 2.0 * R * h - h * h)); // std::clamp(v, lo, hi) lo>hi'de UB. Mikro-freze/bozuk takım (cap<0.05 veya cap=0) → koru. return std::clamp(so, 0.05, std::max(0.05, t.cap)); // uc yaricapiyla sinirli } // AdaptivePathDropCutter'i tek path uzerinde calistir, ciktiyi kolineer filtrele. std::vector dropCutterCalistir(ocl::STLSurf& surf, const ocl::MillingCutter* kesici, const ocl::Path& path, f64 altZ, f64 ornek, f64 minOrnek) { ocl::AdaptivePathDropCutter apdc; apdc.setSTL(surf); apdc.setCutter(kesici); apdc.setPath(&path); apdc.setSampling(std::max(0.02, ornek)); apdc.setMinSampling(std::max(0.002, minOrnek)); apdc.setZ(altZ); apdc.run(); const std::vector ham = apdc.getPoints(); if (ham.size() < 3) return ham; ocl::LineCLFilter f; f.setTolerance(std::max(0.001, minOrnek * 0.5)); for (const ocl::CLPoint& c : ham) f.addCLPoint(c); f.run(); return std::vector(f.clpoints.begin(), f.clpoints.end()); } // CL nokta dizisini kesme yoluna cevir (giris/cikis dahil). Bos → hicbir sey. void clPolyEkle(TakimYolu& yol, const CamIslem& p, const std::vector& pts) { if (pts.size() < 2) return; hamle(yol, HamleTip::Hizli, pts[0].x, pts[0].y, p.guvenliZ); hamle(yol, HamleTip::Dalma, pts[0].x, pts[0].y, std::max(pts[0].z, p.altZ), p.dalmaIlerleme); for (size_t i = 1; i < pts.size(); ++i) hamle(yol, HamleTip::Kesme, pts[i].x, pts[i].y, std::max(pts[i].z, p.altZ), p.ilerleme); hamle(yol, HamleTip::Hizli, pts.back().x, pts.back().y, p.guvenliZ); } // Kapali poligonu ocl Path'e cevir (Z=0; drop-cutter oturtur). void poligonPath(ocl::Path& path, const Poligon& poli) { const size_t n = poli.size(); for (size_t i = 0; i < n; ++i) { const Vec2d& a = poli[i]; const Vec2d& b = poli[(i + 1) % n]; path.append(ocl::Line(ocl::Point(a.x, a.y, 0), ocl::Point(b.x, b.y, 0))); } } // XY bolgesi uzerinde aci yonunde zigzag raster path (Z=0). hatve = tarama araligi. void zigzagRaster(ocl::Path& path, const XYSinir& s, f64 aciDerece, f64 hatve) { const f64 aci = aciDerece * std::numbers::pi / 180.0; const Vec2d u{std::cos(aci), std::sin(aci)}; const Vec2d v{-u.y, u.x}; const Vec2d merkez{(s.x0 + s.x1) * 0.5, (s.y0 + s.y1) * 0.5}; const f64 yariKosegen = 0.5 * std::hypot(s.gen(), s.der()); const f64 h = std::max(0.05, hatve); bool ileri = true, ilk = true; Vec2d oncekiUc{}; for (f64 sk = -yariKosegen; sk <= yariKosegen + 1e-9; sk += h, ileri = !ileri) { const Vec2d o = merkez + v * sk; const Vec2d a = o - u * yariKosegen, b = o + u * yariKosegen; const Vec2d bas = ileri ? a : b, son = ileri ? b : a; if (!ilk) // zigzag baglantisi (drop-cutter yuzeyi takip eder) path.append(ocl::Line(ocl::Point(oncekiUc.x, oncekiUc.y, 0), ocl::Point(bas.x, bas.y, 0))); path.append(ocl::Line(ocl::Point(bas.x, bas.y, 0), ocl::Point(son.x, son.y, 0))); oncekiUc = son; ilk = false; } } } // namespace TakimYolu profilYolu(const CamIslem& p, const Takim& t, const std::vector& konturlar) { TakimYolu yol; if (konturlar.empty() || konturlar[0].size() < 3) return yol; const f64 r = t.cap * 0.5; f64 delta = 0; if (p.ofsetYon == OfsetYonu::Sol) delta = r + p.yuzeyPayi; // dis (sol) telafi else if (p.ofsetYon == OfsetYonu::Sag) delta = -(r + p.yuzeyPayi); // ic (sag) telafi // Takim yaricap telafili kontur (OCCT exact wire offset). Merkez → kontur ustunde. const PoligonKumesi ofset = std::abs(delta) < 1e-9 ? PoligonKumesi{konturlar[0]} : poligonOfset({konturlar[0]}, delta); if (ofset.empty()) return yol; const std::vector zs = zPasolari(p); for (const Poligon& path : ofset) { if (path.size() < 3) continue; const KapaliYol k(path); const Vec2d bas = k.n[0]; hamle(yol, HamleTip::Hizli, bas.x, bas.y, p.guvenliZ); hamle(yol, HamleTip::Hizli, bas.x, bas.y, p.geriCekmeZ); f64 oncekiZ = p.ustZ; for (const f64 z : zs) { girisYap(yol, p, t, k, oncekiZ, z); konturKes(yol, p, k, z, true); oncekiZ = z; } hamle(yol, HamleTip::Hizli, bas.x, bas.y, p.guvenliZ); } yol.istatistikGuncelle(); return yol; } TakimYolu cepYolu(const CamIslem& p, const Takim& t, const std::vector& konturlar, const HesapGeri& geri) { TakimYolu yol; if (konturlar.empty() || konturlar[0].size() < 3) return yol; const f64 r = t.cap * 0.5; const f64 kenarPay = r + p.yuzeyPayi; const f64 hatve = std::max(0.2, p.yanalHatveOran * t.cap); // Islenebilir bolge (OCCT exact): dis kontur iceri, adalar disari ofsetlenip cikarilir. PoligonKumesi bolge = poligonOfset({konturlar[0]}, -kenarPay); if (konturlar.size() > 1) { const PoligonKumesi adalar = poligonOfset(altKume(konturlar, 1, konturlar.size()), kenarPay); if (!adalar.empty()) bolge = poligonFark(bolge, adalar); } if (bolge.empty()) return yol; // Ic ice exact ofset halkalari (seviye 0 = sinir, artan = iceri). std::vector seviyeler{bolge}; for (int k = 1; k < 4000; ++k) { PoligonKumesi halka = poligonOfset(bolge, -hatve * (f64)k); if (halka.empty()) break; seviyeler.push_back(std::move(halka)); } const std::vector zs = zPasolari(p); f64 oncekiZ = p.ustZ; bool ilkKonum = true; Vec2d son{}; for (size_t zi = 0; zi < zs.size(); ++zi) { if (geri.iptal && geri.iptal()) break; const f64 z = zs[zi]; bool zdeIlk = true; // Icten disa: en ic halka once (takim yuku dengeli, temiz duvar son). for (size_t li = seviyeler.size(); li-- > 0;) { for (const Poligon& path : seviyeler[li]) { if (path.size() < 3) continue; const KapaliYol k(path); const Vec2d bas = k.n[0]; const bool uzak = ilkKonum || (bas - son).uzunluk() > 2.5 * hatve + 1e-9; if (zdeIlk || uzak) { // Yeni bolge/paso: geri cek, konumlan, giris yap. if (!ilkKonum) hamle(yol, HamleTip::Hizli, son.x, son.y, p.guvenliZ); hamle(yol, HamleTip::Hizli, bas.x, bas.y, p.guvenliZ); hamle(yol, HamleTip::Hizli, bas.x, bas.y, p.geriCekmeZ); girisYap(yol, p, t, k, oncekiZ, z); // oncekiZ = bir ust pasonun tabani } else { hamle(yol, HamleTip::Kesme, bas.x, bas.y, z, p.ilerleme); // kisa yanal baglanti } konturKes(yol, p, k, z, false); son = bas; ilkKonum = false; zdeIlk = false; } } oncekiZ = z; if (geri.ilerle) geri.ilerle((f32)(zi + 1) / (f32)zs.size(), "Cep pasosu"); } if (!ilkKonum) hamle(yol, HamleTip::Hizli, son.x, son.y, p.guvenliZ); yol.istatistikGuncelle(); return yol; } TakimYolu delikYolu(const CamIslem& p, const std::vector& merkezler) { TakimYolu yol; if (merkezler.empty()) return yol; // En yakin komsu siralamasi (kisa yol). std::vector sira; std::vector alindi(merkezler.size(), false); size_t simdiki = 0; for (size_t k = 0; k < merkezler.size(); ++k) { alindi[simdiki] = true; sira.push_back(merkezler[simdiki]); f64 enKisa = 1e300; size_t enYakin = simdiki; for (size_t i = 0; i < merkezler.size(); ++i) { if (alindi[i]) continue; const f64 m = (merkezler[i] - merkezler[simdiki]).uzunlukKare(); if (m < enKisa) { enKisa = m; enYakin = i; } } simdiki = enYakin; } for (const Vec3d& m : sira) { hamle(yol, HamleTip::Hizli, m.x, m.y, p.guvenliZ); hamle(yol, HamleTip::Hizli, m.x, m.y, p.geriCekmeZ); if (p.dongu == DelikDongu::PeckDelme && p.peckDerinlik > 0.05) { f64 z = p.ustZ; while (z > p.altZ + 1e-9) { z = std::max(p.altZ, z - p.peckDerinlik); hamle(yol, HamleTip::Dalma, m.x, m.y, z, p.dalmaIlerleme); if (z > p.altZ + 1e-9) hamle(yol, HamleTip::Hizli, m.x, m.y, p.geriCekmeZ); } } else { hamle(yol, HamleTip::Dalma, m.x, m.y, p.altZ, p.dalmaIlerleme); } hamle(yol, HamleTip::Hizli, m.x, m.y, p.geriCekmeZ); yol.delikler.push_back({m.x, m.y, p.ustZ}); } hamle(yol, HamleTip::Hizli, sira.back().x, sira.back().y, p.guvenliZ); yol.istatistikGuncelle(); return yol; } TakimYolu kaba3DYolu(const CamIslem& p, const Takim& t, const MeshGirdi& mesh, const HesapGeri& geri) { TakimYolu yol; if (mesh.indeksler.size() < 3) return yol; ocl::STLSurf surf; meshYukle(surf, mesh); if (surf.size() == 0) return yol; const std::unique_ptr temel = kesiciOlustur(t); // Yuzey payi: ofsetli (buyutulmus) kesici ayni yuzeyden pay kadar uzak durur. const std::unique_ptr ofsetli( p.yuzeyPayi > 1e-6 ? temel->offsetCutter(p.yuzeyPayi) : nullptr); ocl::AdaptiveWaterline wl; wl.setSTL(surf); wl.setCutter(ofsetli ? ofsetli.get() : temel.get()); wl.setSampling(std::max(0.05, p.ornekleme)); wl.setMinSampling(std::max(0.005, p.minOrnekleme)); // Cep-ici doldurma yanal hatvesi (waterline halkasindan iceri OCCT exact ofset). const f64 dolduruHatve = std::max(0.3, p.yanalHatveOran * t.cap); const std::vector zs = zPasolari(p); for (size_t zi = 0; zi < zs.size(); ++zi) { if (geri.iptal && geri.iptal()) break; wl.reset(); wl.setZ(zs[zi]); wl.run(); const std::vector> loops = wl.getLoops(); // (a) Sinir (waterline) pasolari — temiz duvar. PoligonKumesi sinirPoli; for (const std::vector& loop : loops) { if (loop.size() < 3) continue; hamle(yol, HamleTip::Hizli, loop[0].x, loop[0].y, p.guvenliZ); hamle(yol, HamleTip::Hizli, loop[0].x, loop[0].y, zs[zi] + std::max(0.5, p.zHatve)); hamle(yol, HamleTip::Dalma, loop[0].x, loop[0].y, loop[0].z, p.dalmaIlerleme); for (size_t i = 1; i < loop.size(); ++i) hamle(yol, HamleTip::Kesme, loop[i].x, loop[i].y, loop[i].z, p.ilerleme); hamle(yol, HamleTip::Kesme, loop[0].x, loop[0].y, loop[0].z, p.ilerleme); hamle(yol, HamleTip::Hizli, loop[0].x, loop[0].y, p.guvenliZ); Poligon poli; poli.reserve(loop.size()); for (const ocl::Point& q : loop) poli.push_back({q.x, q.y}); sinirPoli.push_back(std::move(poli)); } // (b) Cep-ici doldurma: waterline halkalarini iceri ofsetleyip sabit-Z temizle. // Loop'lar birlesik dolu bolgeye getirilir (OCCT fuse), sonra iceri exact ofset. if (p.cepDoldur && !sinirPoli.empty()) { const PoligonKumesi dolu = poligonBirlesim(sinirPoli); for (int k = 1; k < 4000; ++k) { PoligonKumesi halka = poligonOfset(dolu, -dolduruHatve * (f64)k); if (halka.empty()) break; for (const Poligon& path : halka) { if (path.size() < 3) continue; const KapaliYol ky(path); const Vec2d bas = ky.n[0]; hamle(yol, HamleTip::Hizli, bas.x, bas.y, p.guvenliZ); hamle(yol, HamleTip::Hizli, bas.x, bas.y, zs[zi] + std::max(0.5, p.zHatve)); hamle(yol, HamleTip::Dalma, bas.x, bas.y, zs[zi], p.dalmaIlerleme); for (size_t i = 0; i < ky.n.size(); ++i) hamle(yol, HamleTip::Kesme, ky.n[(i + 1) % ky.n.size()].x, ky.n[(i + 1) % ky.n.size()].y, zs[zi], p.ilerleme); hamle(yol, HamleTip::Hizli, bas.x, bas.y, p.guvenliZ); } } } if (geri.ilerle) geri.ilerle((f32)(zi + 1) / (f32)zs.size(), "Waterline + cep doldurma"); } yol.istatistikGuncelle(); return yol; } TakimYolu finis3DYolu(const CamIslem& p, const Takim& t, const MeshGirdi& mesh, const HesapGeri& geri) { TakimYolu yol; if (mesh.indeksler.size() < 3) return yol; ocl::STLSurf surf; meshYukle(surf, mesh); if (surf.size() == 0) return yol; const std::unique_ptr temel = kesiciOlustur(t); const std::unique_ptr ofsetli( p.yuzeyPayi > 1e-6 ? temel->offsetCutter(p.yuzeyPayi) : nullptr); const ocl::MillingCutter* kesici = ofsetli ? ofsetli.get() : temel.get(); // Mesh XY siniri uzerinde scallop-tabanli hatveyle acili zigzag raster. const XYSinir s = xySinir(mesh, t.cap * 0.5); if (geri.ilerle) geri.ilerle(0.2f, "Raster kuruluyor (scallop hatve)"); ocl::Path path; zigzagRaster(path, s, p.finisAciDerece, scallopHatve(p, t)); if (geri.ilerle) geri.ilerle(0.35f, "Drop-cutter calisiyor"); const std::vector pts = dropCutterCalistir(surf, kesici, path, p.altZ, p.ornekleme, p.minOrnekleme); if (pts.empty()) return yol; if (geri.ilerle) geri.ilerle(0.9f, "Yol donusturuluyor"); clPolyEkle(yol, p, pts); yol.istatistikGuncelle(); return yol; } // ---- Strateji 2: Kabadan Kalan (Rest Roughing) ---- // Onceki buyuk takim capiyla waterline "dolu bolge" (o takimin isleyebildigi) cikarilir; // simdiki takimla dolu bolge iceri ofsetlenir. Fark = kalan malzeme. Yalnizca farkta // (rest) sabit-Z temizleme yapilir → gereksiz hava kesimi yok. TakimYolu restKabaYolu(const CamIslem& p, const Takim& t, const MeshGirdi& mesh, const HesapGeri& geri) { TakimYolu yol; if (mesh.indeksler.size() < 3) return yol; ocl::STLSurf surf; meshYukle(surf, mesh); if (surf.size() == 0) return yol; const std::unique_ptr temel = kesiciOlustur(t); const std::unique_ptr ofsetli( p.yuzeyPayi > 1e-6 ? temel->offsetCutter(p.yuzeyPayi) : nullptr); const ocl::MillingCutter* kesici = ofsetli ? ofsetli.get() : temel.get(); // Onceki buyuk takim (silindirik yaklastir) — kalan alan hesabi icin. const f64 oncekiR = std::max(t.cap * 0.5 + 0.1, p.oncekiTakimCap * 0.5); const f64 buyukEk = oncekiR - t.cap * 0.5; // buyuk takimin fazladan biraktigi et const f64 hatve = std::max(0.3, p.yanalHatveOran * t.cap); ocl::AdaptiveWaterline wl; wl.setSTL(surf); wl.setCutter(kesici); wl.setSampling(std::max(0.05, p.ornekleme)); wl.setMinSampling(std::max(0.005, p.minOrnekleme)); const std::vector zs = zPasolari(p); for (size_t zi = 0; zi < zs.size(); ++zi) { if (geri.iptal && geri.iptal()) break; wl.reset(); wl.setZ(zs[zi]); wl.run(); PoligonKumesi simdiki; for (const std::vector& loop : wl.getLoops()) { if (loop.size() < 3) continue; Poligon poli; poli.reserve(loop.size()); for (const ocl::Point& q : loop) poli.push_back({q.x, q.y}); simdiki.push_back(std::move(poli)); } if (simdiki.empty()) continue; const PoligonKumesi dolu = poligonBirlesim(simdiki); // Onceki buyuk takimin bu Z'de isleyemedigi bolge = dolu ⊖ (dolu iceri buyukEk ofset). const PoligonKumesi oncekiErisim = poligonOfset(dolu, -buyukEk); PoligonKumesi kalan = poligonFark(dolu, oncekiErisim); if (kalan.empty()) continue; // Kalan seride ic-ofset pasolarla temizle (sabit-Z). for (int k = 0; k < 4000; ++k) { PoligonKumesi halka = k == 0 ? kalan : poligonOfset(kalan, -hatve * (f64)k); if (halka.empty()) break; for (const Poligon& path : halka) { if (path.size() < 3) continue; const KapaliYol ky(path); const Vec2d bas = ky.n[0]; hamle(yol, HamleTip::Hizli, bas.x, bas.y, p.guvenliZ); hamle(yol, HamleTip::Hizli, bas.x, bas.y, zs[zi] + std::max(0.5, p.zHatve)); hamle(yol, HamleTip::Dalma, bas.x, bas.y, zs[zi], p.dalmaIlerleme); for (size_t i = 0; i < ky.n.size(); ++i) hamle(yol, HamleTip::Kesme, ky.n[(i + 1) % ky.n.size()].x, ky.n[(i + 1) % ky.n.size()].y, zs[zi], p.ilerleme); hamle(yol, HamleTip::Hizli, bas.x, bas.y, p.guvenliZ); } } if (geri.ilerle) geri.ilerle((f32)(zi + 1) / (f32)zs.size(), "Kalan malzeme temizleniyor"); } yol.istatistikGuncelle(); return yol; } // ---- Strateji 4: Sweep Finiş ---- // Surus polyline'lari (yuzey akis egrileri) drop-cutter ile yuzeye oturtulur. // Her surus egrisi ayri bir path → temiz linking (giris/cikis guvenli Z'den). TakimYolu sweepFinisYolu(const CamIslem& p, const Takim& t, const MeshGirdi& mesh, const std::vector>& surus, const HesapGeri& geri) { TakimYolu yol; if (mesh.indeksler.size() < 3 || surus.empty()) return yol; ocl::STLSurf surf; meshYukle(surf, mesh); if (surf.size() == 0) return yol; const std::unique_ptr temel = kesiciOlustur(t); const std::unique_ptr ofsetli( p.yuzeyPayi > 1e-6 ? temel->offsetCutter(p.yuzeyPayi) : nullptr); const ocl::MillingCutter* kesici = ofsetli ? ofsetli.get() : temel.get(); for (size_t si = 0; si < surus.size(); ++si) { if (geri.iptal && geri.iptal()) break; const std::vector& egri = surus[si]; if (egri.size() < 2) continue; ocl::Path path; for (size_t i = 0; i + 1 < egri.size(); ++i) path.append(ocl::Line(ocl::Point(egri[i].x, egri[i].y, 0), ocl::Point(egri[i + 1].x, egri[i + 1].y, 0))); const std::vector pts = dropCutterCalistir(surf, kesici, path, p.altZ, p.ornekleme, p.minOrnekleme); clPolyEkle(yol, p, pts); if (geri.ilerle) geri.ilerle((f32)(si + 1) / (f32)surus.size(), "Sweep pasosu"); } yol.istatistikGuncelle(); return yol; } // ---- Strateji 5: Advanced Finiş (eğime duyarlı birleşik) ---- // Ucgen normalinden yuzey egimi hesaplanir. Dik ucgenler (egim > esik) waterline // (sabit-Z, dik duvar takibi); sig ucgenler paralel drop-cutter ile taranir. // Iki yol birlestirilir → tek operasyonda kaliteli birlesik finis. TakimYolu advancedFinisYolu(const CamIslem& p, const Takim& t, const MeshGirdi& mesh, const HesapGeri& geri) { TakimYolu yol; if (mesh.indeksler.size() < 3) return yol; ocl::STLSurf surf; meshYukle(surf, mesh); if (surf.size() == 0) return yol; const std::unique_ptr temel = kesiciOlustur(t); const std::unique_ptr ofsetli( p.yuzeyPayi > 1e-6 ? temel->offsetCutter(p.yuzeyPayi) : nullptr); const ocl::MillingCutter* kesici = ofsetli ? ofsetli.get() : temel.get(); // Dik bolge Z araligi: normali yatayla ~dik olan ucgenlerin Z kapsami. const f64 cosEsik = std::cos((90.0 - p.dikEsikDerece) * std::numbers::pi / 180.0); f64 dikZmin = 1e300, dikZmax = -1e300; bool dikVar = false; for (size_t i = 0; i + 2 < mesh.indeksler.size(); i += 3) { const Vec3d& a = mesh.noktalar[mesh.indeksler[i]]; const Vec3d& b = mesh.noktalar[mesh.indeksler[i + 1]]; const Vec3d& c = mesh.noktalar[mesh.indeksler[i + 2]]; const Vec3d nrm = (b - a).capraz(c - a); const f64 boy = nrm.uzunluk(); if (boy < 1e-9) continue; const f64 nz = std::abs(nrm.z / boy); // |cos(normal, Z)| → 0 = dik duvar if (nz < cosEsik) { // dik bolge dikVar = true; for (const Vec3d& v : {a, b, c}) { dikZmin = std::min(dikZmin, v.z); dikZmax = std::max(dikZmax, v.z); } } } // (a) Dik bolge → waterline (sabit-Z pasolar, dik duvar temiz). if (dikVar && dikZmax > dikZmin + 1e-6) { if (geri.ilerle) geri.ilerle(0.15f, "Dik bölge (waterline)"); ocl::AdaptiveWaterline wl; wl.setSTL(surf); wl.setCutter(kesici); wl.setSampling(std::max(0.05, p.ornekleme)); wl.setMinSampling(std::max(0.005, p.minOrnekleme)); const f64 adim = std::max(0.05, scallopHatve(p, t)); // dik: adim = scallop-hatve for (f64 z = dikZmax; z > dikZmin - 1e-9; z -= adim) { if (geri.iptal && geri.iptal()) break; wl.reset(); wl.setZ(std::max(dikZmin, z)); wl.run(); for (const std::vector& loop : wl.getLoops()) { if (loop.size() < 3) continue; hamle(yol, HamleTip::Hizli, loop[0].x, loop[0].y, p.guvenliZ); hamle(yol, HamleTip::Dalma, loop[0].x, loop[0].y, loop[0].z, p.dalmaIlerleme); for (size_t i = 1; i < loop.size(); ++i) hamle(yol, HamleTip::Kesme, loop[i].x, loop[i].y, loop[i].z, p.ilerleme); hamle(yol, HamleTip::Kesme, loop[0].x, loop[0].y, loop[0].z, p.ilerleme); hamle(yol, HamleTip::Hizli, loop[0].x, loop[0].y, p.guvenliZ); } } } // (b) Sig bolge → paralel drop-cutter raster (tum mesh; sig yerlerde etkin). if (geri.ilerle) geri.ilerle(0.6f, "Sığ bölge (paralel drop-cutter)"); const XYSinir s = xySinir(mesh, t.cap * 0.5); ocl::Path path; zigzagRaster(path, s, p.finisAciDerece, scallopHatve(p, t)); const std::vector pts = dropCutterCalistir(surf, kesici, path, p.altZ, p.ornekleme, p.minOrnekleme); clPolyEkle(yol, p, pts); yol.istatistikGuncelle(); return yol; } // ---- Strateji 6: Kontur Güdümlü ---- // Rehber konturdan (surus[0]) ic ve dis esit-stepover ofset pasolar uretilir; // her ofset poligonu drop-cutter ile yuzeye oturtulur → kontur boyunca sabit stepover. TakimYolu konturGudumluYolu(const CamIslem& p, const Takim& t, const MeshGirdi& mesh, const std::vector>& surus, const HesapGeri& geri) { TakimYolu yol; if (mesh.indeksler.size() < 3 || surus.empty() || surus[0].size() < 3) return yol; ocl::STLSurf surf; meshYukle(surf, mesh); if (surf.size() == 0) return yol; const std::unique_ptr temel = kesiciOlustur(t); const std::unique_ptr ofsetli( p.yuzeyPayi > 1e-6 ? temel->offsetCutter(p.yuzeyPayi) : nullptr); const ocl::MillingCutter* kesici = ofsetli ? ofsetli.get() : temel.get(); const f64 hatve = scallopHatve(p, t); const int paso = std::max(1, p.konturPaso); const PoligonKumesi rehber{surus[0]}; // Rehberden ic ve dis paso ofsetleri (0 = rehberin kendisi). OCCT exact offset. std::vector pasolar; pasolar.push_back(rehber); for (int k = 1; k <= paso; ++k) { PoligonKumesi ic = poligonOfset(rehber, -hatve * (f64)k); if (!ic.empty()) pasolar.push_back(std::move(ic)); PoligonKumesi dis = poligonOfset(rehber, hatve * (f64)k); if (!dis.empty()) pasolar.push_back(std::move(dis)); } for (size_t pi = 0; pi < pasolar.size(); ++pi) { if (geri.iptal && geri.iptal()) break; for (const Poligon& poli : pasolar[pi]) { if (poli.size() < 3) continue; ocl::Path path; poligonPath(path, poli); const std::vector pts = dropCutterCalistir(surf, kesici, path, p.altZ, p.ornekleme, p.minOrnekleme); clPolyEkle(yol, p, pts); } if (geri.ilerle) geri.ilerle((f32)(pi + 1) / (f32)pasolar.size(), "Kontur ofset pasosu"); } yol.istatistikGuncelle(); return yol; } // ---- Strateji 7: Rework / Pencil (Kalan Finiş) ---- // Kucuk takimla, buyuk onceki finis takiminin erisemedigi ic kose/oyuklari temizle. // BatchDropCutter grid heightmap iki capta hesaplanir; fark buyuk (kalan et) veya // CCPoint tipi EDGE (kose temasi) olan noktalar pencil hedefidir. Bu noktalarda // kucuk takim drop-cutter Z'sinde nokta-nokta temizlik uretir. TakimYolu reworkFinisYolu(const CamIslem& p, const Takim& t, const MeshGirdi& mesh, const HesapGeri& geri) { TakimYolu yol; if (mesh.indeksler.size() < 3) return yol; ocl::STLSurf surf; meshYukle(surf, mesh); if (surf.size() == 0) return yol; const std::unique_ptr kucuk = kesiciOlustur(t); // simdiki (kucuk) const f64 buyukCap = std::max(t.cap + 0.5, p.oncekiTakimCap); const std::unique_ptr buyuk = t.tip == TakimTip::KureselFreze ? std::unique_ptr( std::make_unique(buyukCap, std::max(10.0, t.boy))) : std::unique_ptr( std::make_unique(buyukCap, std::max(10.0, t.boy))); // Grid nokta bulutu (mesh XY siniri, kucuk takim scallop-hatvesiyle). const XYSinir s = xySinir(mesh, -t.cap * 0.25); // kenardan hafif iceri if (!s.gecerli()) return yol; const f64 grid = std::max(0.2, scallopHatve(p, t)); auto heightmap = [&](const ocl::MillingCutter* c, std::vector& out) { ocl::BatchDropCutter bdc; // OpenMP paralel bdc.setSTL(surf); bdc.setCutter(c); for (f64 y = s.y0; y <= s.y1 + 1e-9; y += grid) for (f64 x = s.x0; x <= s.x1 + 1e-9; x += grid) { ocl::CLPoint cp(x, y, p.altZ); bdc.appendPoint(cp); } bdc.run(); out = bdc.getCLPoints(); }; if (geri.ilerle) geri.ilerle(0.2f, "Küçük takım heightmap"); std::vector kucukH; heightmap(kucuk.get(), kucukH); if (geri.iptal && geri.iptal()) return yol; if (geri.ilerle) geri.ilerle(0.5f, "Büyük takım heightmap"); std::vector buyukH; heightmap(buyuk.get(), buyukH); // Kalan hedef: kucuk takim buyuk takimdan belirgin asagi inebiliyorsa (dZ) VEYA // temas tipi kenar (EDGE*) ise → pencil bolgesi. Nokta-nokta dalip cikan yol. const f64 esik = std::max(0.05, t.cap * 0.05); if (geri.ilerle) geri.ilerle(0.75f, "Kalan bölge tespiti"); bool ilk = true; for (size_t i = 0; i < kucukH.size() && i < buyukH.size(); ++i) { const ocl::CLPoint& k = kucukH[i]; const f64 dZ = buyukH[i].z - k.z; // kucuk daha asagi → pozitif ocl::CCPoint* cc = k.cc.load(); const bool kenar = cc && (cc->type == ocl::EDGE || cc->type == ocl::EDGE_POS || cc->type == ocl::EDGE_NEG || cc->type == ocl::EDGE_CYL); if (dZ < esik && !kenar) continue; // buyuk takim zaten temizlemis const f64 z = std::max(k.z, p.altZ); if (ilk) { hamle(yol, HamleTip::Hizli, k.x, k.y, p.guvenliZ); hamle(yol, HamleTip::Dalma, k.x, k.y, z, p.dalmaIlerleme); ilk = false; } else { hamle(yol, HamleTip::Kesme, k.x, k.y, z, p.ilerleme); } } if (!ilk && !yol.hamleler.empty()) hamle(yol, HamleTip::Hizli, yol.hamleler.back().nokta.x, yol.hamleler.back().nokta.y, p.guvenliZ); yol.istatistikGuncelle(); return yol; } } // namespace fiko::cam