#include "fiko/sketch/eskiz_veri.hpp" #include #include #include #include // CATIA "Operation" 2B duzenleme araclari. SAF 2B matematik; hicbiri coz() // cagirmaz — cagiran katman duzenleme sonrasi coz() eder. Tum metotlar // dogrudan p_[] / geo_[] uzerinde calisir (ayni sinif → private erisim). namespace fiko::eskiz { namespace { constexpr Skalar kEps = 1e-9; constexpr Skalar kPi = 3.14159265358979323846; // Kesisim parametrelerini birlestirme/karsilastirma toleransi. kEps (1e-9) // GEOMETRIK filtre icin fazla siki: ayni noktada ust uste binen kesisimler ya da // tam kesisime tiklamada kararsiz davranisa yol acar. Parametre-uzayi toleransi. constexpr Skalar kKesTol = 1e-7; // Sirali kesisim listesinden, tik parametresi tk'yi ICEREN [alt, ust] araligini // bulur. Sinir listesi 0 ve 1'i (uc noktalar) icermeli. Tam bir sinirin uzerine // tiklandiginda (cakisik/kesisim noktasi) parca sifir-boya cokmeye BIRAKILMAZ: // komsu gercek araliga (once sag, yoksa sol) kayilir. dizi sirali+tekil olmali. inline void aralikBul(const std::vector& sinir, Skalar tk, Skalar& alt, Skalar& ust) { alt = sinir.front(); ust = sinir.back(); for (std::size_t i = 0; i + 1 < sinir.size(); ++i) { Skalar a = sinir[i], b = sinir[i + 1]; if (tk >= a - kKesTol && tk <= b + kKesTol) { alt = a; ust = b; // Tik tam bir sinirin uzerinde (sifir-uzunluk risk): dolu komsu araligi sec. if (ust - alt < kKesTol) { if (i + 2 < sinir.size()) { alt = sinir[i + 1]; ust = sinir[i + 2]; } else if (i >= 1) { alt = sinir[i - 1]; ust = sinir[i]; } } return; } } } // Sirala + toleransta tekillestir (ayni noktadaki kesisimler tek sinir olur). inline void siralaTekille(std::vector& v) { std::sort(v.begin(), v.end()); v.erase(std::unique(v.begin(), v.end(), [](Skalar a, Skalar b) { return std::abs(a - b) < kKesTol; }), v.end()); } struct V2 { Skalar x = 0, y = 0; }; inline V2 operator-(V2 a, V2 b) { return {a.x - b.x, a.y - b.y}; } inline V2 operator+(V2 a, V2 b) { return {a.x + b.x, a.y + b.y}; } inline V2 operator*(V2 a, Skalar s) { return {a.x * s, a.y * s}; } inline Skalar nokta(V2 a, V2 b) { return a.x * b.x + a.y * b.y; } inline Skalar capraz(V2 a, V2 b) { return a.x * b.y - a.y * b.x; } inline Skalar uzunluk(V2 a) { return std::sqrt(a.x * a.x + a.y * a.y); } // t'yi [0,1] araligina bagli mi (kucuk tolerans dahil). inline bool araliktaT(Skalar t) { return t >= -kEps && t <= 1 + kEps; } // ---- Cizgi-cizgi kesisim: P = A + t*(B-A) = C + u*(D-C). t,u [0,1] segment // icidir; sonsuz dogru icin t,u serbest. donerT/donerU segment parametreleri. bool cizgiCizgiKes(V2 A, V2 B, V2 C, V2 D, Skalar& donerT, Skalar& donerU, V2& kesisim) { V2 r = B - A, s = D - C; Skalar payda = capraz(r, s); if (std::abs(payda) < kEps) return false; // paralel V2 ac = C - A; donerT = capraz(ac, s) / payda; donerU = capraz(ac, r) / payda; kesisim = A + r * donerT; return true; } // ---- Cizgi-cember kesisim. Merkez M, yaricap R. Cizgi A->B (sonsuz dogru). // 0/1/2 kesisim, her biri icin cizgi parametresi t (A + t*(B-A)) doner. int cizgiCemberKes(V2 A, V2 B, V2 M, Skalar R, Skalar tOut[2]) { V2 d = B - A; V2 f = A - M; Skalar a = nokta(d, d); if (a < kEps) return 0; Skalar b = 2 * nokta(f, d); Skalar c = nokta(f, f) - R * R; Skalar disc = b * b - 4 * a * c; if (disc < -kEps) return 0; if (disc < kEps) { tOut[0] = -b / (2 * a); return 1; } Skalar sd = std::sqrt(disc); tOut[0] = (-b - sd) / (2 * a); tOut[1] = (-b + sd) / (2 * a); return 2; } // ---- Cember-cember kesisim. Merkez/yaricap (M1,R1)-(M2,R2). 0/1/2 kesisim; // 2 gercek kesisimde noktalar pOut[] (dunya koord) doldurulur. Teget (1) ve // ayrik/ic-ice (0) durumlar gercek kesisim sayilmaz. int cemberCemberKes(V2 M1, Skalar R1, V2 M2, Skalar R2, V2 pOut[2]) { V2 d = M2 - M1; Skalar dd = uzunluk(d); if (dd < kEps) return 0; // es merkez if (dd > R1 + R2 - kEps) return 0; // ayrik / dis teget if (dd < std::abs(R1 - R2) + kEps) return 0; // ic ice / ic teget Skalar a = (R1 * R1 - R2 * R2 + dd * dd) / (2 * dd); Skalar h2 = R1 * R1 - a * a; if (h2 < kEps) return 1; // teget → gercek kesisim degil Skalar h = std::sqrt(h2); V2 u{d.x / dd, d.y / dd}; V2 P = M1 + u * a; V2 perp{-u.y, u.x}; pOut[0] = P + perp * h; pOut[1] = P - perp * h; return 2; } } // namespace // ---- yardimcilar -------------------------------------------------------- Geometri* EskizVeri::geoBulMut(u32 kimlik) { for (auto& g : geo_) if (g.kimlik == kimlik) return &g; return nullptr; } void EskizVeri::kisitSuruAyarla(u32 kisitKimlik, bool suru) { for (auto& k : kisit_) if (k.kimlik == kisitKimlik) { k.suru = suru; return; } } void EskizVeri::kisitDegerAyarla(u32 kisitKimlik, Skalar deger) { for (auto& k : kisit_) if (k.kimlik == kisitKimlik) { k.deger = deger; return; } } void EskizVeri::noktaTasi(int ix, int iy, Skalar x, Skalar y) { if (ix >= 0 && ix < (int)p_.size()) p_[(std::size_t)ix] = x; if (iy >= 0 && iy < (int)p_.size()) p_[(std::size_t)iy] = y; } // ---- Trim / Uzat -------------------------------------------------------- // CATIA "Trim/Quick Trim" DOGRU davranisi: tiklanan alt-parca, onu SINIRLAYAN en // yakin gercek kesisimlere kadar KALDIRILIR. Asla uzak bir uc noktayi tasiyip // kaynastirma yapilmaz — yalniz tiklanan parametre araligindaki parca kirpilir. // - Iki kesisim arasi tiklandi → o araliktaki parca silinir (cizgi ikiye boler). // - Tik ile bir uc arasi (tek kesisim) → o parca kirpilir (uc kesisime ceker). // - Hic kesisim yoksa → tum geometri silinir. // Bu, tikin oldugu parcayi gercek kesisim topolojisiyle keser; hizliKirp ile ayni // cekirdek mantik (kesisim sinirlari) — trim tiklanan parcayi hedefler. bool EskizVeri::trim(u32 geo, Skalar tikU, Skalar tikV) { return hizliKirp(geo, tikU, tikV); } bool EskizVeri::uzat(u32 geo, Skalar tikU, Skalar tikV) { Geometri* g = geoBulMut(geo); if (!g || g->tip != GeoTip::Cizgi) return false; V2 A{px(g->a.ix), px(g->a.iy)}; V2 B{px(g->b.ix), px(g->b.iy)}; V2 tik{tikU, tikV}; V2 d = B - A; Skalar dd = nokta(d, d); if (dd < kEps) return false; Skalar tTik = nokta(tik - A, d) / dd; bool tikAUcunda = tTik < 0.5; // Uzatma: sonsuz dogru boyunca, segment DISINDA (t<0 veya t>1) en yakin // hedef kesisimi bul (tikin oldugu ucun otesinde). Skalar enIyi = std::numeric_limits::max(); bool bulundu = false; V2 kesNokta{}; auto degerlendir = [&](Skalar t, V2 kp) { // Uzatma yonu: tik A ucunda → t<0 tarafi; B ucunda → t>1 tarafi. bool dogruYon = tikAUcunda ? (t < -kEps) : (t > 1 + kEps); if (!dogruYon) return; Skalar mesafe = tikAUcunda ? (-t) : (t - 1); if (mesafe < enIyi) { enIyi = mesafe; kesNokta = kp; bulundu = true; } }; for (const auto& o : geo_) { if (o.kimlik == geo) continue; if (o.tip == GeoTip::Cizgi) { V2 C{px(o.a.ix), px(o.a.iy)}; V2 Dd{px(o.b.ix), px(o.b.iy)}; Skalar t, u; V2 kp; if (!cizgiCizgiKes(A, B, C, Dd, t, u, kp)) continue; if (!araliktaT(u)) continue; // hedef segment ici olmali degerlendir(t, kp); } else if (o.tip == GeoTip::Daire || o.tip == GeoTip::Yay) { V2 M{px(o.merkez.ix), px(o.merkez.iy)}; Skalar R = px(o.ir); Skalar tt[2]; int n = cizgiCemberKes(A, B, M, R, tt); for (int i = 0; i < n; ++i) degerlendir(tt[i], A + d * tt[i]); } } if (!bulundu) return false; if (tikAUcunda) noktaTasi(g->a.ix, g->a.iy, kesNokta.x, kesNokta.y); else noktaTasi(g->b.ix, g->b.iy, kesNokta.x, kesNokta.y); return true; } // ---- Bol --------------------------------------------------------------- u32 EskizVeri::bol(u32 geo, Skalar u, Skalar v) { Geometri* g = geoBulMut(geo); if (!g) return 0; if (g->tip == GeoTip::Cizgi) { V2 A{px(g->a.ix), px(g->a.iy)}; V2 B{px(g->b.ix), px(g->b.iy)}; V2 P{u, v}; // P'yi cizgiye izdusur (bol noktasi cizgi ustunde olmayabilir). V2 d = B - A; Skalar dd = nokta(d, d); if (dd < kEps) return 0; Skalar t = nokta(P - A, d) / dd; V2 kesim = A + d * t; bool yap = g->yapi; // Yeni parca: kesim -> B. Orijinal: A -> kesim. u32 yeni = cizgiEkle(kesim.x, kesim.y, B.x, B.y); if (yap) yapiAyarla(yeni, true); // geoBulMut sonrasi cizgiEkle vektoru yeniden tahsis edebilir → tekrar bul. g = geoBulMut(geo); noktaTasi(g->b.ix, g->b.iy, kesim.x, kesim.y); // Orijinalin eski b ucu artik 'yeni' parcaya ait → o uca bagli kisitlari // yeni'ye tasi (kopan uc geri cekilmesin). kisitBoluntuRemap(geo, yeni); return yeni; } if (g->tip == GeoTip::Yay) { V2 M{px(g->merkez.ix), px(g->merkez.iy)}; Skalar R = px(g->ir); Skalar a2 = px(g->ib2); V2 P{u, v}; Skalar am = std::atan2(P.y - M.y, P.x - M.x); // Yeni parca: am -> a2. Orijinal: a1 -> am. bool yap = g->yapi; u32 yeni = yayEkle(M.x, M.y, R, am, a2); if (yap) yapiAyarla(yeni, true); g = geoBulMut(geo); // orijinal bitis acisini am yap + uc noktasini guncelle. p_[(std::size_t)g->ib2] = am; noktaTasi(g->b.ix, g->b.iy, M.x + R * std::cos(am), M.y + R * std::sin(am)); kisitBoluntuRemap(geo, yeni); // eski b (bitis) ucu yeni parcaya ait return yeni; } return 0; // Daire bolunmesi MVP disi (once trim/kapat ile yay yapilir) } // ---- Hizli Kirp (Quick Trim / Break and Rubber In) --------------------- namespace { // Bir aciyi [taban, taban+2pi) araligina normalize eder. inline Skalar aciNorm(Skalar a, Skalar taban) { while (a < taban) a += 2 * kPi; while (a >= taban + 2 * kPi) a -= 2 * kPi; return a; } } // namespace bool EskizVeri::hizliKirp(u32 geo, Skalar u, Skalar v, KirpMod mod) { Geometri* g = geoBulMut(geo); if (!g) return false; // ----------------------------- CIZGI ----------------------------------- if (g->tip == GeoTip::Cizgi) { V2 A{px(g->a.ix), px(g->a.iy)}; V2 B{px(g->b.ix), px(g->b.iy)}; V2 tik{u, v}; V2 d = B - A; Skalar dd = nokta(d, d); if (dd < kEps) return false; // Tiklanan noktanin cizgi parametresi. Skalar tk = nokta(tik - A, d) / dd; // Kesen komsu geometrilerin cizgi-parametreleri (segment ici 0..1). // Uc noktalari da sinir kabul et → aralikBul icin 0 ve 1 daima listede. std::vector kesim{0.0, 1.0}; for (const auto& o : geo_) { if (o.kimlik == geo) continue; if (o.tip == GeoTip::Cizgi) { V2 C{px(o.a.ix), px(o.a.iy)}; V2 Dd{px(o.b.ix), px(o.b.iy)}; Skalar t, uu; V2 kp; if (!cizgiCizgiKes(A, B, C, Dd, t, uu, kp)) continue; if (!araliktaT(uu)) continue; // kesici segment ici if (t < kKesTol || t > 1 - kKesTol) continue; // uclar sinir olarak zaten var kesim.push_back(t); } else if (o.tip == GeoTip::Daire || o.tip == GeoTip::Yay) { V2 M{px(o.merkez.ix), px(o.merkez.iy)}; Skalar R = px(o.ir); Skalar tt[2]; int n = cizgiCemberKes(A, B, M, R, tt); // Teget (tek kok, disc~0) yalniz DEGER, GERCEK kesisim degil → atla. if (n == 1) continue; for (int i = 0; i < n; ++i) { Skalar t = tt[i]; if (t < kKesTol || t > 1 - kKesTol) continue; // Yay ise kesisim noktasi yay acisi araliginda mi kontrol et. if (o.tip == GeoTip::Yay && o.ib1 >= 0 && o.ib2 >= 0) { V2 kp = A + d * t; Skalar ka = std::atan2(kp.y - M.y, kp.x - M.x); Skalar a1 = px(o.ib1), a2 = px(o.ib2); Skalar taban = std::min(a1, a2); Skalar acKp = aciNorm(ka, taban); if (acKp < std::min(a1, a2) - kEps || acKp > std::max(a1, a2) + kEps) continue; } kesim.push_back(t); } } } // Sirala + ayni noktadaki kesisimleri tekille (cakisik/ust uste kararli). siralaTekille(kesim); // --- Break and Keep: tum ic kesisimlerden bol, hicbir parcayi silme. --- if (mod == KirpMod::Tut) { if (kesim.size() <= 2) return false; // ic kesisim yok → bolunecek sinir yok u32 parca = geo; // her bolme yeni parcayi dondurur; kalan sinirlar onun icinde for (std::size_t i = 1; i + 1 < kesim.size(); ++i) { V2 P = A + d * kesim[i]; u32 yeni = bol(parca, P.x, P.y); if (!yeni) break; parca = yeni; } return true; } // Tiklanan segmentin [tAlt, tUst] araligini bul (tik tam kesisimde ise // sifir-boya cokmez — komsu dolu araliga kayar). Skalar tAlt = 0.0, tUst = 1.0; aralikBul(kesim, tk, tAlt, tUst); // --- Break and Rubber Out: tiklanan parca KALIR, disindakiler silinir. --- if (mod == KirpMod::DisSil) { bool degisti = false; if (tAlt > kEps) { // A tarafi kirpiliyor → A ucunu tAlt'a cek V2 P = A + d * tAlt; kisitUcTemizle(geo, 0); noktaTasi(g->a.ix, g->a.iy, P.x, P.y); degisti = true; } if (tUst < 1 - kEps) { // B tarafi kirpiliyor → B ucunu tUst'e cek V2 P = A + d * tUst; kisitUcTemizle(geo, 1); noktaTasi(g->b.ix, g->b.iy, P.x, P.y); degisti = true; } return degisti; // kesisim yoksa degisiklik yok } // --- Break and Rubber In (varsayilan): tiklanan parca silinir. --- if (tAlt <= kEps && tUst >= 1 - kEps) { geoSil(geo); // hic kesisim yok → tum cizgi return true; } if (tAlt <= kEps) { // sol uc, sagda kesisim → A ucunu tUst'e tasi V2 P = A + d * tUst; kisitUcTemizle(geo, 0); // kesilen A ucundaki eski baglanti temizlenir noktaTasi(g->a.ix, g->a.iy, P.x, P.y); return true; } if (tUst >= 1 - kEps) { // sag uc, solda kesisim → B ucunu tAlt'a tasi V2 P = A + d * tAlt; kisitUcTemizle(geo, 1); // kesilen B ucundaki eski baglanti temizlenir noktaTasi(g->b.ix, g->b.iy, P.x, P.y); return true; } // Ortada: orijinal A..tAlt, yeni parca tUst..B. V2 Palt = A + d * tAlt; V2 Pust = A + d * tUst; bool yap = g->yapi; u32 yeni = cizgiEkle(Pust.x, Pust.y, B.x, B.y); if (yap) yapiAyarla(yeni, true); g = geoBulMut(geo); // cizgiEkle yeniden tahsis edebilir noktaTasi(g->b.ix, g->b.iy, Palt.x, Palt.y); kisitBoluntuRemap(geo, yeni); // eski b ucu yeni parcaya ait → kisit tasi return true; } // ------------------------------- YAY ------------------------------------ if (g->tip == GeoTip::Yay) { if (g->ib1 < 0 || g->ib2 < 0) return false; V2 M{px(g->merkez.ix), px(g->merkez.iy)}; Skalar R = px(g->ir); Skalar a1 = px(g->ib1), a2 = px(g->ib2); Skalar aMin = std::min(a1, a2), aMax = std::max(a1, a2); // Tiklanan aci. Skalar tk = aciNorm(std::atan2(v - M.y, u - M.x), aMin); // Kesen komsu geometrilerin yay uzerindeki acilarini topla. // Yay uc acilari da sinir → aralikBul icin aMin/aMax daima listede. std::vector kesim{aMin, aMax}; for (const auto& o : geo_) { if (o.kimlik == geo) continue; if (o.tip == GeoTip::Cizgi) { V2 C{px(o.a.ix), px(o.a.iy)}; V2 Dd{px(o.b.ix), px(o.b.iy)}; Skalar tt[2]; int n = cizgiCemberKes(C, Dd, M, R, tt); if (n == 1) continue; // teget → gercek kesisim degil for (int i = 0; i < n; ++i) { if (!araliktaT(tt[i])) continue; // kesici segment ici V2 kp = C + (Dd - C) * tt[i]; Skalar ka = aciNorm(std::atan2(kp.y - M.y, kp.x - M.x), aMin); if (ka < aMin + kKesTol || ka > aMax - kKesTol) continue; // yay ici (uclar zaten var) kesim.push_back(ka); } } else if (o.tip == GeoTip::Daire || o.tip == GeoTip::Yay) { // Komsu cember/yay kesisimi (aksi halde daireyle kesisen yay "kesisim yok" // sanip TUM YAYI silerdi). V2 M2{px(o.merkez.ix), px(o.merkez.iy)}; Skalar R2 = px(o.ir); V2 pp[2]; int n = cemberCemberKes(M, R, M2, R2, pp); if (n < 2) continue; for (int i = 0; i < n; ++i) { // Komsu YAY ise kesisim onun aci araliginda mi? if (o.tip == GeoTip::Yay && o.ib1 >= 0 && o.ib2 >= 0) { Skalar b1 = px(o.ib1), b2 = px(o.ib2), tb = std::min(b1, b2); Skalar ac2 = aciNorm(std::atan2(pp[i].y - M2.y, pp[i].x - M2.x), tb); if (ac2 < std::min(b1, b2) - kEps || ac2 > std::max(b1, b2) + kEps) continue; } Skalar ka = aciNorm(std::atan2(pp[i].y - M.y, pp[i].x - M.x), aMin); if (ka < aMin + kKesTol || ka > aMax - kKesTol) continue; // bu yayin ici kesim.push_back(ka); } } } // Sirala + tekille (cakisik kesisimlerde kararli), tik'i iceren aralik. siralaTekille(kesim); // --- Break and Keep: ic kesisim acilarindan bol, silme yok. --- if (mod == KirpMod::Tut) { if (kesim.size() <= 2) return false; // ic kesisim yok const bool yap = g->yapi; u32 son = 0; for (std::size_t i = 1; i + 1 < kesim.size(); ++i) { u32 yeni = yayEkle(M.x, M.y, R, kesim[i], kesim[i + 1]); if (yap) yapiAyarla(yeni, true); son = yeni; } g = geoBulMut(geo); // yayEkle yeniden tahsis edebilir p_[(std::size_t)g->ib2] = kesim[1]; noktaTasi(g->b.ix, g->b.iy, M.x + R * std::cos(kesim[1]), M.y + R * std::sin(kesim[1])); if (son) kisitBoluntuRemap(geo, son); // eski b (aMax) ucu SON parcaya ait return true; } Skalar aAlt = aMin, aUst = aMax; aralikBul(kesim, tk, aAlt, aUst); // --- Break and Rubber Out: tiklanan yay parcasi kalir, disi silinir. --- if (mod == KirpMod::DisSil) { bool degisti = false; if (aAlt > aMin + kEps) { // baslangic tarafi kirpiliyor kisitUcTemizle(geo, 0); p_[(std::size_t)g->ib1] = aAlt; noktaTasi(g->a.ix, g->a.iy, M.x + R * std::cos(aAlt), M.y + R * std::sin(aAlt)); degisti = true; } if (aUst < aMax - kEps) { // bitis tarafi kirpiliyor kisitUcTemizle(geo, 1); p_[(std::size_t)g->ib2] = aUst; noktaTasi(g->b.ix, g->b.iy, M.x + R * std::cos(aUst), M.y + R * std::sin(aUst)); degisti = true; } return degisti; } if (aAlt <= aMin + kEps && aUst >= aMax - kEps) { geoSil(geo); // hic kesisim → tum yay return true; } if (aAlt <= aMin + kEps) { // baslangic tarafi → baslangici aUst'e cek kisitUcTemizle(geo, 0); // kesilen baslangic (a) ucundaki eski baglanti p_[(std::size_t)g->ib1] = aUst; noktaTasi(g->a.ix, g->a.iy, M.x + R * std::cos(aUst), M.y + R * std::sin(aUst)); return true; } if (aUst >= aMax - kEps) { // bitis tarafi → bitisi aAlt'e cek kisitUcTemizle(geo, 1); // kesilen bitis (b) ucundaki eski baglanti p_[(std::size_t)g->ib2] = aAlt; noktaTasi(g->b.ix, g->b.iy, M.x + R * std::cos(aAlt), M.y + R * std::sin(aAlt)); return true; } // Ortada: orijinal aMin..aAlt, yeni yay aUst..aMax. bool yap = g->yapi; u32 yeni = yayEkle(M.x, M.y, R, aUst, aMax); if (yap) yapiAyarla(yeni, true); g = geoBulMut(geo); p_[(std::size_t)g->ib2] = aAlt; noktaTasi(g->b.ix, g->b.iy, M.x + R * std::cos(aAlt), M.y + R * std::sin(aAlt)); kisitBoluntuRemap(geo, yeni); // eski b (bitis) ucu yeni parcaya ait return true; } // ------------------------------ DAIRE ----------------------------------- // Tam daire kapali dongu → uc yok. Kesisim acilari daireyi parcalara boler; // tiklanan parcanin (dairesel gap) cikarilmasiyla daire YERINDE bir YAYA // donusur (merkez/yaricap parametreleri korunur → radyus/merkez kisitlari sag). if (g->tip == GeoTip::Daire) { V2 M{px(g->merkez.ix), px(g->merkez.iy)}; Skalar R = px(g->ir); if (R < kEps) return false; const Skalar tk = aciNorm(std::atan2(v - M.y, u - M.x), 0.0); // [0,2pi) std::vector kesim; // kesisim acilari [0,2pi) for (const auto& o : geo_) { if (o.kimlik == geo) continue; if (o.tip == GeoTip::Cizgi) { V2 C{px(o.a.ix), px(o.a.iy)}, Dd{px(o.b.ix), px(o.b.iy)}; Skalar tt[2]; int n = cizgiCemberKes(C, Dd, M, R, tt); if (n < 2) continue; // teget/yok for (int i = 0; i < n; ++i) { if (!araliktaT(tt[i])) continue; // kesici segment ici V2 kp = C + (Dd - C) * tt[i]; kesim.push_back(aciNorm(std::atan2(kp.y - M.y, kp.x - M.x), 0.0)); } } else if (o.tip == GeoTip::Daire || o.tip == GeoTip::Yay) { V2 M2{px(o.merkez.ix), px(o.merkez.iy)}; Skalar R2 = px(o.ir); V2 pp[2]; int n = cemberCemberKes(M, R, M2, R2, pp); if (n < 2) continue; for (int i = 0; i < n; ++i) { // Komsu YAY ise kesisim onun aci araliginda mi kontrol et. if (o.tip == GeoTip::Yay && o.ib1 >= 0 && o.ib2 >= 0) { Skalar a1 = px(o.ib1), a2 = px(o.ib2); Skalar taban = std::min(a1, a2); Skalar acKp = aciNorm(std::atan2(pp[i].y - M2.y, pp[i].x - M2.x), taban); if (acKp < std::min(a1, a2) - kEps || acKp > std::max(a1, a2) + kEps) continue; } kesim.push_back(aciNorm(std::atan2(pp[i].y - M.y, pp[i].x - M.x), 0.0)); } } } siralaTekille(kesim); // Kapali daireyi kirpmak icin en az 2 sinir (kesisim) gerekir. Yoksa daireyi // YOK ETME — islem uygulanamaz (kullanici tum daireyi kaybetmemeli). if (kesim.size() < 2) return false; // --- Break and Keep: kesisim acilarindan parcalara bol (daire → n yay). --- if (mod == KirpMod::Tut) { const std::size_t nk = kesim.size(); const bool yap = g->yapi; for (std::size_t i = 1; i < nk; ++i) { const Skalar b0 = kesim[i]; const Skalar b1 = (i + 1 < nk) ? kesim[i + 1] : kesim[0] + 2 * kPi; u32 yeni = yayEkle(M.x, M.y, R, b0, b1); if (yap) yapiAyarla(yeni, true); } g = geoBulMut(geo); // yayEkle yeniden tahsis edebilir g->tip = GeoTip::Yay; // ilk dilim: daire yerinde yaya doner g->ib1 = paramEkle(kesim[0]); g->ib2 = paramEkle(kesim[1]); g->a = Nokta{paramEkle(M.x + R * std::cos(kesim[0])), paramEkle(M.y + R * std::sin(kesim[0]))}; g->b = Nokta{paramEkle(M.x + R * std::cos(kesim[1])), paramEkle(M.y + R * std::sin(kesim[1]))}; return true; } // tk'yi iceren dairesel gap [aAlt, aUst] (son→ilk sarmasi dahil). const std::size_t n = kesim.size(); Skalar aAlt = kesim[0], aUst = kesim[1]; for (std::size_t i = 0; i < n; ++i) { Skalar a = kesim[i]; Skalar b = (i + 1 < n) ? kesim[i + 1] : kesim[0] + 2 * kPi; Skalar t = (i + 1 == n && tk < a) ? tk + 2 * kPi : tk; // sarma diliminde kaydir if (t >= a - kKesTol && t <= b + kKesTol) { aAlt = a; aUst = b; break; } } // Kalan yay — IcSil: tiklanan gap CIKAR (aUst → aAlt+2pi); DisSil: tiklanan // gap KALIR (aAlt → aUst). Daireyi yerinde yaya cevir. const Skalar yb = (mod == KirpMod::DisSil) ? aAlt : aUst; const Skalar ye = (mod == KirpMod::DisSil) ? aUst : aAlt + 2 * kPi; g->tip = GeoTip::Yay; g->ib1 = paramEkle(yb); g->ib2 = paramEkle(ye); g->a = Nokta{paramEkle(M.x + R * std::cos(yb)), paramEkle(M.y + R * std::sin(yb))}; g->b = Nokta{paramEkle(M.x + R * std::cos(ye)), paramEkle(M.y + R * std::sin(ye))}; return true; } return false; } // ---- Kose yuvarlatma (fillet) ------------------------------------------ // Fillet/pah köşesini TRIM için serbestleştirir — iki çekişme kaynağını giderir: // (1) gA↔gB köşe Çakışıklık/NoktaÜzerinde kısıtını SİLER (aksi halde uçlar teğet/pah // noktasına taşınınca coz() çakışıklığı geri dayatıp geometriyi köşeye çeker). // (2) Kırpılan çizginin TEK-geo uzunluk ölçüsünü (Mesafe/YatayMesafe/DikeyMesafe) // REFERANSA çevirir (aksi halde coz() eski uzunluğu geri getirip çizgiyi KAYDIRIR — // kullanıcının "üçgen/kısıtlı sketch'te kaydırma + trim yok" bug'ı). Ölçü silinmez, // referans kalır (yeni uzunluğu gösterir, sürücü değildir → çekişme yok). // CATIA davranışı: fillet keskin köşeyi + ona bağlı köşe kısıtını kaldırır. Kısıt yoksa no-op. static void koseSerbestlestir(EskizVeri& e, u32 gA, u32 gB, bool trimA, bool trimB) { const auto olcuMu = [](KisitTip t) { return t == KisitTip::Mesafe || t == KisitTip::YatayMesafe || t == KisitTip::DikeyMesafe; }; std::vector silCak; // silinecek çakışıklık kısıtları std::vector refYap; // referansa çevrilecek uzunluk ölçüleri for (const Kisit& k : e.kisitlar()) { if ((k.tip == KisitTip::Cakisik || k.tip == KisitTip::NoktaUzerinde) && k.geo.size() >= 2) { bool aVar = false, bVar = false; for (u32 g : k.geo) { if (g == gA) aVar = true; if (g == gB) bVar = true; } if (aVar && bVar) silCak.push_back(k.kimlik); // köşedeki iki çizgiyi bağlayan çakışıklık } else if (olcuMu(k.tip) && k.geo.size() == 1 && k.suru) { if ((trimA && k.geo[0] == gA) || (trimB && k.geo[0] == gB)) refYap.push_back(k.kimlik); } } for (u32 id : silCak) e.kisitSil(id); for (u32 id : refYap) e.kisitSuruAyarla(id, false); // sürücü→referans (çekişmez) } u32 EskizVeri::koseYuvarlat(u32 geoA, u32 geoB, Skalar r, TrimMod mod) { Geometri* gA = geoBulMut(geoA); Geometri* gB = geoBulMut(geoB); if (!gA || !gB || gA->tip != GeoTip::Cizgi || gB->tip != GeoTip::Cizgi) return 0; V2 A1{px(gA->a.ix), px(gA->a.iy)}, A2{px(gA->b.ix), px(gA->b.iy)}; V2 B1{px(gB->a.ix), px(gB->a.iy)}, B2{px(gB->b.ix), px(gB->b.iy)}; // Iki dogrunun kesisim koseSi. Skalar t, u2; V2 kose; if (!cizgiCizgiKes(A1, A2, B1, B2, t, u2, kose)) return 0; // paralel // Trim yapılacaksa köşeyi serbestleştir (çakışıklık sil + uzunluk ölçüsü referansa). // "No Trim"de köşe olduğu gibi kaldığından kısıtlar korunur. if (mod != TrimMod::Yok) koseSerbestlestir(*this, geoA, geoB, true, mod == TrimMod::Hepsi); // Her cizgide, koseDEN uzaklasan birim yon (kose disina). auto yon = [&](V2 P1, V2 P2) -> V2 { // kose'ye yakin ucu bul, oradan diger uca dogru birim. Skalar d1 = uzunluk(P1 - kose), d2 = uzunluk(P2 - kose); V2 uzak = (d1 > d2) ? P1 : P2; V2 dv = uzak - kose; Skalar L = uzunluk(dv); if (L < kEps) return {0, 0}; return {dv.x / L, dv.y / L}; }; V2 uA = yon(A1, A2); V2 uB = yon(B1, B2); if (uzunluk(uA) < kEps || uzunluk(uB) < kEps) return 0; // Iki yon arasi yari-aci. Teget mesafe = r / tan(theta/2). Skalar kosTheta = std::max(-1.0, std::min(1.0, nokta(uA, uB))); Skalar theta = std::acos(kosTheta); if (theta < kEps || std::abs(theta - kPi) < kEps) return 0; // dogru/ust uste Skalar tegetMes = r / std::tan(theta / 2.0); // Teget noktalar (yaya deger). V2 TA = kose + uA * tegetMes; V2 TB = kose + uB * tegetMes; // Yay merkezi: aci ortayin uzerinde, kose'den mesafe = r / sin(theta/2). V2 ortay{uA.x + uB.x, uA.y + uB.y}; Skalar oL = uzunluk(ortay); if (oL < kEps) return 0; ortay = ortay * (1.0 / oL); Skalar merkezMes = r / std::sin(theta / 2.0); V2 M = kose + ortay * merkezMes; Skalar a1 = std::atan2(TA.y - M.y, TA.x - M.x); Skalar a2 = std::atan2(TB.y - M.y, TB.x - M.x); // MINOR yay CCW normalizasyonu: fillet yayı her zaman < π (span = π-theta). Ham atan2 // farkı π'yi aşabilir / ters işaretli olabilir → hem render (lineer a1→a2) hem BRep (CCW // a1→a2 zorlar) YANLIŞ/reflex yay çizer + birbiriyle tutarsız olur. Farkı (-π,π]'ye indir, // negatifse uçları takasla (CCW minor garanti), a2 = a1 + |d| → iki tüketici de aynı minor yay. Skalar d = a2 - a1; while (d <= -kPi) d += 2.0 * kPi; while (d > kPi) d -= 2.0 * kPi; if (d < 0.0) { std::swap(a1, a2); d = -d; } a2 = a1 + d; u32 yay = yayEkle(M.x, M.y, r, a1, a2); // Cizgileri teget noktalara kadar kisalt: kose'ye yakin ucu TA/TB'ye tasi. // Trim modu: Hepsi→ikisi de, Ilk→sadece gA, Yok→hicbiri (sadece yay eklenir). gA = geoBulMut(geoA); gB = geoBulMut(geoB); auto koseUcunaTasi = [&](Geometri* g, V2 hedef) { V2 Pa{px(g->a.ix), px(g->a.iy)}, Pb{px(g->b.ix), px(g->b.iy)}; if (uzunluk(Pa - kose) < uzunluk(Pb - kose)) noktaTasi(g->a.ix, g->a.iy, hedef.x, hedef.y); else noktaTasi(g->b.ix, g->b.iy, hedef.x, hedef.y); }; if (mod != TrimMod::Yok) koseUcunaTasi(gA, TA); if (mod == TrimMod::Hepsi) koseUcunaTasi(gB, TB); return yay; } // ---- Pah (chamfer) ----------------------------------------------------- u32 EskizVeri::pah(u32 geoA, u32 geoB, Skalar mesafe, TrimMod mod) { // Tek-mesafe (esit kenar) → iki-mesafe imzasina delege (mukerrer degil). return pah(geoA, geoB, mesafe, mesafe, mod); } u32 EskizVeri::pah(u32 geoA, u32 geoB, Skalar mesafeA, Skalar mesafeB, TrimMod mod) { Geometri* gA = geoBulMut(geoA); Geometri* gB = geoBulMut(geoB); if (!gA || !gB || gA->tip != GeoTip::Cizgi || gB->tip != GeoTip::Cizgi) return 0; V2 A1{px(gA->a.ix), px(gA->a.iy)}, A2{px(gA->b.ix), px(gA->b.iy)}; V2 B1{px(gB->a.ix), px(gB->a.iy)}, B2{px(gB->b.ix), px(gB->b.iy)}; Skalar t, u2; V2 kose; if (!cizgiCizgiKes(A1, A2, B1, B2, t, u2, kose)) return 0; // Trim yapılacaksa köşeyi serbestleştir (çakışıklık sil + uzunluk ölçüsü referansa). if (mod != TrimMod::Yok) koseSerbestlestir(*this, geoA, geoB, true, mod == TrimMod::Hepsi); auto yon = [&](V2 P1, V2 P2) -> V2 { Skalar d1 = uzunluk(P1 - kose), d2 = uzunluk(P2 - kose); V2 uzak = (d1 > d2) ? P1 : P2; V2 dv = uzak - kose; Skalar L = uzunluk(dv); if (L < kEps) return {0, 0}; return {dv.x / L, dv.y / L}; }; V2 uA = yon(A1, A2), uB = yon(B1, B2); if (uzunluk(uA) < kEps || uzunluk(uB) < kEps) return 0; // Iki kenar farkli uzunluk olabilir (CATIA First+Second Length / Angle+Length). V2 PA = kose + uA * mesafeA; V2 PB = kose + uB * mesafeB; u32 cizgi = cizgiEkle(PA.x, PA.y, PB.x, PB.y); gA = geoBulMut(geoA); gB = geoBulMut(geoB); auto koseUcunaTasi = [&](Geometri* g, V2 hedef) { V2 Pa{px(g->a.ix), px(g->a.iy)}, Pb{px(g->b.ix), px(g->b.iy)}; if (uzunluk(Pa - kose) < uzunluk(Pb - kose)) noktaTasi(g->a.ix, g->a.iy, hedef.x, hedef.y); else noktaTasi(g->b.ix, g->b.iy, hedef.x, hedef.y); }; // Trim modu: Hepsi→ikisi de, Ilk→sadece gA, Yok→hicbiri (sadece pah cizgisi). if (mod != TrimMod::Yok) koseUcunaTasi(gA, PA); if (mod == TrimMod::Hepsi) koseUcunaTasi(gB, PB); return cizgi; } // ---- Donusum yardimcilari ---------------------------------------------- // Bir geometriyi, nokta-donusum fonksiyonu ile YENI geometri olarak ekler. // (yay icin aci donusumu ozel — merkez + iki uc noktadan yeniden acilar). static u32 geoKopyala(EskizVeri& e, const Geometri& g, const std::function& f) { switch (g.tip) { case GeoTip::Cizgi: { V2 A{e.px(g.a.ix), e.px(g.a.iy)}, B{e.px(g.b.ix), e.px(g.b.iy)}; A = f(A); B = f(B); u32 k = e.cizgiEkle(A.x, A.y, B.x, B.y); if (g.yapi) e.yapiAyarla(k, true); return k; } case GeoTip::Daire: { V2 M{e.px(g.merkez.ix), e.px(g.merkez.iy)}; Skalar R = e.px(g.ir); // Olcek etkisini yakala: birim vektorun donusum sonrasi boyu. V2 M2 = f(M); V2 kenar = f({M.x + R, M.y}); Skalar R2 = uzunluk(V2{kenar.x - M2.x, kenar.y - M2.y}); u32 k = e.daireEkle(M2.x, M2.y, R2); if (g.yapi) e.yapiAyarla(k, true); return k; } case GeoTip::Yay: { V2 M{e.px(g.merkez.ix), e.px(g.merkez.iy)}; V2 A{e.px(g.a.ix), e.px(g.a.iy)}, B{e.px(g.b.ix), e.px(g.b.iy)}; V2 M2 = f(M), A2 = f(A), B2 = f(B); Skalar R2 = uzunluk(V2{A2.x - M2.x, A2.y - M2.y}); Skalar a1 = std::atan2(A2.y - M2.y, A2.x - M2.x); Skalar a2 = std::atan2(B2.y - M2.y, B2.x - M2.x); u32 k = e.yayEkle(M2.x, M2.y, R2, a1, a2); if (g.yapi) e.yapiAyarla(k, true); return k; } case GeoTip::Spline: { // Gecis noktalari (a + ic + b) SIRAYLA donusturulup yeni spline kurulur. // (default dali spline'i NOKTA'ya dejenere ediyordu — ayna/dizi bozuyordu.) std::vector> gecis; gecis.reserve(g.ekNoktalar.size() + 2); const auto ekle = [&](const Nokta& n) { V2 p{e.px(n.ix), e.px(n.iy)}; p = f(p); gecis.emplace_back(p.x, p.y); }; ekle(g.a); for (const Nokta& n : g.ekNoktalar) ekle(n); ekle(g.b); u32 k = e.splineEkle(gecis); if (g.yapi) e.yapiAyarla(k, true); return k; } default: { V2 A{e.px(g.a.ix), e.px(g.a.iy)}; A = f(A); u32 k = e.noktaEkle(A.x, A.y); if (g.yapi) e.yapiAyarla(k, true); return k; } } } // Var olan bir geometriyi YERINDE tasir (kopyalamadan). static void geoTasiYerinde(EskizVeri& e, Geometri& g, const std::function& f) { auto tasi = [&](const Nokta& n) { if (!n.gecerli()) return; V2 p{e.px(n.ix), e.px(n.iy)}; p = f(p); e.noktaTasi(n.ix, n.iy, p.x, p.y); }; tasi(g.a); tasi(g.b); tasi(g.merkez); for (const Nokta& n : g.ekNoktalar) tasi(n); // Spline ic gecis noktalari // Yay/daire: yaricap ve acilari uctan tazele. if (g.tip == GeoTip::Yay) { V2 M{e.px(g.merkez.ix), e.px(g.merkez.iy)}; V2 A{e.px(g.a.ix), e.px(g.a.iy)}, B{e.px(g.b.ix), e.px(g.b.iy)}; if (g.ir >= 0) e.noktaTasi(g.ir, -1, uzunluk(V2{A.x - M.x, A.y - M.y}), 0); if (g.ib1 >= 0) e.noktaTasi(g.ib1, -1, std::atan2(A.y - M.y, A.x - M.x), 0); if (g.ib2 >= 0) e.noktaTasi(g.ib2, -1, std::atan2(B.y - M.y, B.x - M.x), 0); } } // ---- Ayna / Simetri ----------------------------------------------------- // Bir cizgi eksenine gore yansitma fonksiyonu uretir. static std::function aynaFonksiyon(V2 A, V2 B) { V2 d = B - A; Skalar L2 = nokta(d, d); return [=](V2 p) -> V2 { if (L2 < kEps) return p; V2 ap = p - A; Skalar t = nokta(ap, d) / L2; V2 izd = A + d * t; // eksen uzerine izdusum return izd + (izd - p); // izdusumun oteki tarafi }; } std::vector EskizVeri::aynala(const std::vector& geolar, u32 eksenGeo) { std::vector yeni; const Geometri* eks = geoBul(eksenGeo); if (!eks || eks->tip != GeoTip::Cizgi) return yeni; V2 A{px(eks->a.ix), px(eks->a.iy)}, B{px(eks->b.ix), px(eks->b.iy)}; auto f = aynaFonksiyon(A, B); // Onbellek: kopyalama vektoru yeniden tahsis eder → kimlikle calis. for (u32 gk : geolar) { const Geometri* g = geoBul(gk); if (!g) continue; Geometri kopya = *g; // deger kopyasi (indeksler eski p_'ye isaret eder; // geoKopyala okuma icin kullanir, tahsis oncesi) yeni.push_back(geoKopyala(*this, kopya, f)); } return yeni; } void EskizVeri::simetriTasi(const std::vector& geolar, u32 eksenGeo) { const Geometri* eks = geoBul(eksenGeo); if (!eks || eks->tip != GeoTip::Cizgi) return; V2 A{px(eks->a.ix), px(eks->a.iy)}, B{px(eks->b.ix), px(eks->b.iy)}; auto f = aynaFonksiyon(A, B); for (u32 gk : geolar) { Geometri* g = geoBulMut(gk); if (g) geoTasiYerinde(*this, *g, f); } } // ---- Ötele / Döndür / Ölçekle ------------------------------------------ std::vector EskizVeri::otele(const std::vector& geolar, Skalar dx, Skalar dy, int kopya) { auto f = [=](V2 p) -> V2 { return {p.x + dx, p.y + dy}; }; std::vector yeni; if (kopya <= 0) { for (u32 gk : geolar) { Geometri* g = geoBulMut(gk); if (g) geoTasiYerinde(*this, *g, f); } return yeni; } for (int i = 1; i <= kopya; ++i) { auto fi = [=](V2 p) -> V2 { return {p.x + dx * i, p.y + dy * i}; }; for (u32 gk : geolar) { const Geometri* g = geoBul(gk); if (!g) continue; Geometri kopyaG = *g; yeni.push_back(geoKopyala(*this, kopyaG, fi)); } } return yeni; } std::vector EskizVeri::dondur(const std::vector& geolar, Skalar mx, Skalar my, Skalar aciRad, int kopya) { auto rot = [](V2 p, Skalar mxx, Skalar myy, Skalar a) -> V2 { Skalar c = std::cos(a), s = std::sin(a); Skalar x = p.x - mxx, y = p.y - myy; return {mxx + x * c - y * s, myy + x * s + y * c}; }; std::vector yeni; if (kopya <= 0) { auto f = [=](V2 p) { return rot(p, mx, my, aciRad); }; for (u32 gk : geolar) { Geometri* g = geoBulMut(gk); if (g) geoTasiYerinde(*this, *g, f); } return yeni; } for (int i = 1; i <= kopya; ++i) { Skalar a = aciRad * i; auto fi = [=](V2 p) { return rot(p, mx, my, a); }; for (u32 gk : geolar) { const Geometri* g = geoBul(gk); if (!g) continue; Geometri kopyaG = *g; yeni.push_back(geoKopyala(*this, kopyaG, fi)); } } return yeni; } std::vector EskizVeri::olcekle(const std::vector& geolar, Skalar mx, Skalar my, Skalar oran, int kopya) { auto sc = [=](V2 p, Skalar o) -> V2 { return {mx + (p.x - mx) * o, my + (p.y - my) * o}; }; std::vector yeni; if (kopya <= 0) { auto f = [=](V2 p) { return sc(p, oran); }; for (u32 gk : geolar) { Geometri* g = geoBulMut(gk); if (g) geoTasiYerinde(*this, *g, f); } return yeni; } for (int i = 1; i <= kopya; ++i) { Skalar o = std::pow(oran, (Skalar)i); auto fi = [=](V2 p) { return sc(p, o); }; for (u32 gk : geolar) { const Geometri* g = geoBul(gk); if (!g) continue; Geometri kopyaG = *g; yeni.push_back(geoKopyala(*this, kopyaG, fi)); } } return yeni; } // ---- Ofset (paralel kopya) --------------------------------------------- // Tek geometrinin ofset kopyasini ekler. Cizgi → paralel oteleme (normal), // daire/yay → es merkezli yaricap +/- mesafe. Yeni kimlik doner (0=basarisiz). static u32 tekOfset(EskizVeri& e, const Geometri& g, Skalar mesafe) { switch (g.tip) { case GeoTip::Cizgi: { V2 A{e.px(g.a.ix), e.px(g.a.iy)}, B{e.px(g.b.ix), e.px(g.b.iy)}; V2 d = B - A; Skalar L = uzunluk(d); if (L < kEps) return 0; V2 n{-d.y / L, d.x / L}; // sol normal V2 A2 = A + n * mesafe, B2 = B + n * mesafe; u32 k = e.cizgiEkle(A2.x, A2.y, B2.x, B2.y); if (g.yapi) e.yapiAyarla(k, true); return k; } case GeoTip::Daire: { V2 M{e.px(g.merkez.ix), e.px(g.merkez.iy)}; Skalar R = e.px(g.ir) + mesafe; if (R < kEps) return 0; u32 k = e.daireEkle(M.x, M.y, R); if (g.yapi) e.yapiAyarla(k, true); return k; } case GeoTip::Yay: { V2 M{e.px(g.merkez.ix), e.px(g.merkez.iy)}; Skalar R = e.px(g.ir) + mesafe; if (R < kEps) return 0; u32 k = e.yayEkle(M.x, M.y, R, e.px(g.ib1), e.px(g.ib2)); if (g.yapi) e.yapiAyarla(k, true); return k; } default: return 0; } } std::vector EskizVeri::ofsetle(const std::vector& geolar, Skalar mesafe, bool ciftTaraf) { std::vector yeni; for (u32 gk : geolar) { const Geometri* g = geoBul(gk); if (!g) continue; Geometri kopyaG = *g; u32 k = tekOfset(*this, kopyaG, mesafe); if (k) yeni.push_back(k); if (ciftTaraf) { const Geometri* g2 = geoBul(gk); // yeniden tahsis olabilir if (g2) { Geometri k2 = *g2; u32 kk = tekOfset(*this, k2, -mesafe); if (kk) yeni.push_back(kk); } } } return yeni; } // ---- Zincir komsulugu (CATIA Offset propagation) -------------------------- // Uc-uca degen komsulari BFS ile zincirler (gecisli). tegetSart ise paylasilan // ucta birim tegetler paralel olmali (capraz carpim ~0): cizgi-yay teget // zinciri. Uc toleransi analiz() ile ayni sabit (1e-3). std::vector EskizVeri::zincirKomsulari(u32 geo, bool tegetSart) const { constexpr Skalar tol = 1e-3; // uc eslestirme (analiz() ile ayni) constexpr Skalar tegTol = 1e-3; // birim teget capraz carpim esigi struct Uc { u32 kimlik; Skalar x, y, tx, ty; }; std::vector uclar; for (const Geometri& g : geo_) { if (g.yapi && g.kimlik != geo) continue; // yapi zincire girmez (tohum haric) if (g.tip == GeoTip::Cizgi) { const V2 A{px(g.a.ix), px(g.a.iy)}, B{px(g.b.ix), px(g.b.iy)}; const V2 d = B - A; const Skalar L = uzunluk(d); if (L < kEps) continue; uclar.push_back({g.kimlik, A.x, A.y, d.x / L, d.y / L}); uclar.push_back({g.kimlik, B.x, B.y, d.x / L, d.y / L}); } else if (g.tip == GeoTip::Yay) { // Uc acilarindan teget (-sin a, cos a); yon isareti teget testinde onemsiz. const Skalar a1 = px(g.ib1), a2 = px(g.ib2); uclar.push_back({g.kimlik, px(g.a.ix), px(g.a.iy), -std::sin(a1), std::cos(a1)}); uclar.push_back({g.kimlik, px(g.b.ix), px(g.b.iy), -std::sin(a2), std::cos(a2)}); } } std::vector sonuc, kuyruk{geo}, gezildi{geo}; const auto gezilmis = [&](u32 k) { return std::find(gezildi.begin(), gezildi.end(), k) != gezildi.end(); }; while (!kuyruk.empty()) { const u32 su = kuyruk.back(); kuyruk.pop_back(); for (const Uc& a : uclar) { if (a.kimlik != su) continue; for (const Uc& b : uclar) { if (b.kimlik == su || gezilmis(b.kimlik)) continue; if (std::hypot(a.x - b.x, a.y - b.y) >= tol) continue; if (tegetSart && std::abs(a.tx * b.ty - a.ty * b.tx) > tegTol) continue; gezildi.push_back(b.kimlik); kuyruk.push_back(b.kimlik); sonuc.push_back(b.kimlik); } } } return sonuc; } // ---- Kapat ------------------------------------------------------------- bool EskizVeri::kapat(u32 geo) { Geometri* g = geoBulMut(geo); if (!g) return false; if (g->tip == GeoTip::Yay) { // Trimlenmis yay → tam cember: ib2 = ib1 + 2pi ve uc noktalar merkez etrafi. if (g->ib1 >= 0 && g->ib2 >= 0) { Skalar a1 = px(g->ib1); p_[(std::size_t)g->ib2] = a1 + 2 * kPi; V2 M{px(g->merkez.ix), px(g->merkez.iy)}; Skalar R = px(g->ir); noktaTasi(g->a.ix, g->a.iy, M.x + R * std::cos(a1), M.y + R * std::sin(a1)); noktaTasi(g->b.ix, g->b.iy, M.x + R * std::cos(a1), M.y + R * std::sin(a1)); } return true; } return false; } // ---- Tumleyen (CATIA Complement) ----------------------------------------- u32 EskizVeri::tumleyen(u32 geo) { Geometri* g = geoBulMut(geo); if (!g || g->tip != GeoTip::Yay || g->ib1 < 0 || g->ib2 < 0) return 0; const Skalar a1 = px(g->ib1), a2 = px(g->ib2); const Skalar lo = std::min(a1, a2), hi = std::max(a1, a2); const Skalar acKapla = hi - lo; // Sifir-boy yay veya tam cember (kapat sonrasi) → tumleyen anlamsiz. if (acKapla < kKesTol || acKapla > 2 * kPi - kKesTol) return 0; const V2 M{px(g->merkez.ix), px(g->merkez.iy)}; const Skalar R = px(g->ir); // Uclari takasla + 2pi sar: [lo,hi] → [hi, lo+2pi]. Ayni iki UC NOKTA, // cemberin OTEKI yayi (gercek BREP acilariyla). p_[(std::size_t)g->ib1] = hi; p_[(std::size_t)g->ib2] = lo + 2 * kPi; noktaTasi(g->a.ix, g->a.iy, M.x + R * std::cos(hi), M.y + R * std::sin(hi)); noktaTasi(g->b.ix, g->b.iy, M.x + R * std::cos(lo), M.y + R * std::sin(lo)); return geo; } // ---- Kesisim noktalari (CATIA Intersection Point destegi) ---------------- // Mevcut anonim-namespace kesisim cekirdegini (cizgiCizgiKes/cizgiCemberKes/ // cemberCemberKes) TEK genel API ile disari acar — app katmani (Kesisim // Noktasi araci vb.) matematik kopyalamaz. bool EskizVeri::kesisimNoktalari(u32 geoA, u32 geoB, std::vector>& out) const { const Geometri* ga = geoBul(geoA); const Geometri* gb = geoBul(geoB); if (!ga || !gb || geoA == geoB) return false; // Yay ise kesisim noktasi yay aci-araliginda olmali (hizliKirp ile ayni kontrol). const auto yayUstunde = [&](const Geometri& g, V2 p) -> bool { if (g.tip != GeoTip::Yay || g.ib1 < 0 || g.ib2 < 0) return true; const V2 M{px(g.merkez.ix), px(g.merkez.iy)}; const Skalar a1 = px(g.ib1), a2 = px(g.ib2); const Skalar ac = aciNorm(std::atan2(p.y - M.y, p.x - M.x), std::min(a1, a2)); return ac >= std::min(a1, a2) - kEps && ac <= std::max(a1, a2) + kEps; }; const auto cemberMi = [](const Geometri& g) { return g.tip == GeoTip::Daire || g.tip == GeoTip::Yay; }; const std::size_t n0 = out.size(); if (ga->tip == GeoTip::Cizgi && gb->tip == GeoTip::Cizgi) { const V2 A{px(ga->a.ix), px(ga->a.iy)}, B{px(ga->b.ix), px(ga->b.iy)}; const V2 C{px(gb->a.ix), px(gb->a.iy)}, D{px(gb->b.ix), px(gb->b.iy)}; Skalar t = 0, u = 0; V2 kp; if (cizgiCizgiKes(A, B, C, D, t, u, kp) && araliktaT(t) && araliktaT(u)) out.push_back({kp.x, kp.y}); } else if ((ga->tip == GeoTip::Cizgi && cemberMi(*gb)) || (cemberMi(*ga) && gb->tip == GeoTip::Cizgi)) { const Geometri& cz = ga->tip == GeoTip::Cizgi ? *ga : *gb; // cizgi const Geometri& cm = ga->tip == GeoTip::Cizgi ? *gb : *ga; // cember/yay const V2 A{px(cz.a.ix), px(cz.a.iy)}, B{px(cz.b.ix), px(cz.b.iy)}; const V2 M{px(cm.merkez.ix), px(cm.merkez.iy)}; Skalar tt[2]; const int n = cizgiCemberKes(A, B, M, px(cm.ir), tt); if (n == 2) { // teget (tek kok) gercek kesisim sayilmaz (hizliKirp kurali) for (int i = 0; i < n; ++i) { if (!araliktaT(tt[i])) continue; const V2 kp = A + (B - A) * tt[i]; if (yayUstunde(cm, kp)) out.push_back({kp.x, kp.y}); } } } else if (cemberMi(*ga) && cemberMi(*gb)) { const V2 M1{px(ga->merkez.ix), px(ga->merkez.iy)}; const V2 M2{px(gb->merkez.ix), px(gb->merkez.iy)}; V2 p[2]; if (cemberCemberKes(M1, px(ga->ir), M2, px(gb->ir), p) == 2) for (int i = 0; i < 2; ++i) if (yayUstunde(*ga, p[i]) && yayUstunde(*gb, p[i])) out.push_back({p[i].x, p[i].y}); } else { return false; // Nokta/Elips kesisimi desteklenmez } return out.size() > n0; } } // namespace fiko::eskiz