#include "fiko/sketch/snap.hpp" #include namespace fiko::eskiz { namespace { constexpr Skalar kPi = 3.14159265358979323846; int oncelik(SnapTip t) { switch (t) { case SnapTip::UcNokta: return 1; case SnapTip::Merkez: return 2; case SnapTip::OrtaNokta: return 3; case SnapTip::Kadran: return 4; case SnapTip::Kesisim: return 5; case SnapTip::NesneUzeri: return 6; case SnapTip::Yatay: return 7; case SnapTip::Dikey: return 7; case SnapTip::Aci: return 8; case SnapTip::Izgara: return 9; default: return 99; } } // Cizgi geometrisinin iki ucu (px ile cozulmus konum). struct Seg { Skalar ax, ay, bx, by; }; Seg segAl(const EskizVeri& e, const Geometri& g) { return Seg{e.px(g.a.ix), e.px(g.a.iy), e.px(g.b.ix), e.px(g.b.iy)}; } } // namespace void SnapMotoru::adaylariTopla(const EskizVeri& e, u32 haricGeo, std::vector& out) const { for (const auto& g : e.geometriler()) { if (g.kimlik == haricGeo) continue; switch (g.tip) { case GeoTip::Nokta: { Skalar ax = e.px(g.a.ix), ay = e.px(g.a.iy); out.push_back({SnapTip::UcNokta, ax, ay, g.kimlik, 0}); break; } case GeoTip::Cizgi: { Seg s = segAl(e, g); out.push_back({SnapTip::UcNokta, s.ax, s.ay, g.kimlik, 0}); out.push_back({SnapTip::UcNokta, s.bx, s.by, g.kimlik, 1}); out.push_back({SnapTip::OrtaNokta, (s.ax + s.bx) * 0.5, (s.ay + s.by) * 0.5, g.kimlik, -1}); break; } case GeoTip::Daire: { Skalar cx = e.px(g.merkez.ix), cy = e.px(g.merkez.iy); Skalar r = e.px(g.ir); out.push_back({SnapTip::Merkez, cx, cy, g.kimlik, 2}); out.push_back({SnapTip::Kadran, cx + r, cy, g.kimlik, -1}); out.push_back({SnapTip::Kadran, cx - r, cy, g.kimlik, -1}); out.push_back({SnapTip::Kadran, cx, cy + r, g.kimlik, -1}); out.push_back({SnapTip::Kadran, cx, cy - r, g.kimlik, -1}); break; } case GeoTip::Yay: { Skalar cx = e.px(g.merkez.ix), cy = e.px(g.merkez.iy); out.push_back({SnapTip::Merkez, cx, cy, g.kimlik, 2}); // Yay uc noktalari. Skalar ax = e.px(g.a.ix), ay = e.px(g.a.iy); Skalar bx = e.px(g.b.ix), by = e.px(g.b.iy); out.push_back({SnapTip::UcNokta, ax, ay, g.kimlik, 0}); out.push_back({SnapTip::UcNokta, bx, by, g.kimlik, 1}); // Yay orta noktasi (aci ortalamasi). Skalar r = e.px(g.ir); Skalar a1 = std::atan2(ay - cy, ax - cx); Skalar a2 = std::atan2(by - cy, bx - cx); Skalar am = (a1 + a2) * 0.5; out.push_back({SnapTip::OrtaNokta, cx + r * std::cos(am), cy + r * std::sin(am), g.kimlik, -1}); break; } } } } bool SnapMotoru::nesneUzeri(const EskizVeri& e, Skalar fu, Skalar fv, u32 haricGeo, SnapSonuc& out) const { bool bulundu = false; Skalar enIyiD2 = aperture * aperture; for (const auto& g : e.geometriler()) { if (g.kimlik == haricGeo) continue; Skalar pu = 0, pv = 0; bool aday = false; if (g.tip == GeoTip::Cizgi) { Seg s = segAl(e, g); Skalar dx = s.bx - s.ax, dy = s.by - s.ay; Skalar uzun2 = dx * dx + dy * dy; if (uzun2 < 1e-12) continue; Skalar t = ((fu - s.ax) * dx + (fv - s.ay) * dy) / uzun2; if (t < 0) t = 0; if (t > 1) t = 1; pu = s.ax + dx * t; pv = s.ay + dy * t; aday = true; } else if (g.tip == GeoTip::Daire || g.tip == GeoTip::Yay) { Skalar cx = e.px(g.merkez.ix), cy = e.px(g.merkez.iy); Skalar r = e.px(g.ir); Skalar a = std::atan2(fv - cy, fu - cx); pu = cx + std::cos(a) * r; pv = cy + std::sin(a) * r; aday = true; } if (!aday) continue; Skalar du = pu - fu, dv = pv - fv; Skalar d2 = du * du + dv * dv; if (d2 <= enIyiD2) { enIyiD2 = d2; out.tip = SnapTip::NesneUzeri; out.u = pu; out.v = pv; out.refGeo = g.kimlik; out.refUc = -1; bulundu = true; } } return bulundu; } bool SnapMotoru::kesisimAra(const EskizVeri& e, Skalar fu, Skalar fv, u32 haricGeo, SnapSonuc& out) const { const auto& geos = e.geometriler(); bool bulundu = false; Skalar enIyiD2 = aperture * aperture; for (std::size_t i = 0; i < geos.size(); ++i) { if (geos[i].tip != GeoTip::Cizgi || geos[i].kimlik == haricGeo) continue; Seg s1 = segAl(e, geos[i]); for (std::size_t j = i + 1; j < geos.size(); ++j) { if (geos[j].tip != GeoTip::Cizgi || geos[j].kimlik == haricGeo) continue; Seg s2 = segAl(e, geos[j]); Skalar r_x = s1.bx - s1.ax, r_y = s1.by - s1.ay; Skalar s_x = s2.bx - s2.ax, s_y = s2.by - s2.ay; Skalar payda = r_x * s_y - r_y * s_x; if (std::abs(payda) < 1e-12) continue; // paralel Skalar qpx = s2.ax - s1.ax, qpy = s2.ay - s1.ay; Skalar t = (qpx * s_y - qpy * s_x) / payda; Skalar u = (qpx * r_y - qpy * r_x) / payda; // Segment sinirlari icinde (kucuk tolerans). if (t < -1e-9 || t > 1 + 1e-9 || u < -1e-9 || u > 1 + 1e-9) continue; Skalar ku = s1.ax + r_x * t; Skalar kv = s1.ay + r_y * t; Skalar du = ku - fu, dv = kv - fv; Skalar d2 = du * du + dv * dv; if (d2 <= enIyiD2) { enIyiD2 = d2; out.tip = SnapTip::Kesisim; out.u = ku; out.v = kv; out.refGeo = 0; out.refUc = -1; bulundu = true; } } } return bulundu; } SnapSonuc SnapMotoru::coz(const EskizVeri& e, Skalar fareU, Skalar fareV, const Skalar* aktifU, const Skalar* aktifV, u32 haricGeo) const { SnapSonuc r; r.tip = SnapTip::Serbest; r.u = fareU; r.v = fareV; const Skalar ap2 = aperture * aperture; // 1) Diskret nokta snap'leri (oncelik + aperture mesafesi). std::vector adaylar; adaylariTopla(e, haricGeo, adaylar); const Aday* enIyi = nullptr; Skalar enIyiD2 = 0; for (const auto& a : adaylar) { Skalar du = a.u - fareU, dv = a.v - fareV; Skalar d2 = du * du + dv * dv; if (d2 > ap2) continue; if (!enIyi || oncelik(a.tip) < oncelik(enIyi->tip) || (oncelik(a.tip) == oncelik(enIyi->tip) && d2 < enIyiD2)) { enIyi = &a; enIyiD2 = d2; } } if (enIyi) { r.tip = enIyi->tip; r.u = enIyi->u; r.v = enIyi->v; r.refGeo = enIyi->geo; r.refUc = enIyi->uc; return r; } // 5) Kesisim (cizgi-cizgi). { SnapSonuc ks; if (kesisimAra(e, fareU, fareV, haricGeo, ks)) return ks; } // 6) Nesne-uzeri (en yakin eleman uzeri nokta). { SnapSonuc no; if (nesneUzeri(e, fareU, fareV, haricGeo, no)) return no; } // 8) Aci snap (aktif nokta varsa) — inference'ten once denenir cunku // aktif nokta acikca cizim baslangici; kullanici acili cizgi bekler. // Ancak yatay/dikey inference (7) daha yuksek oncelik: 0/90/180/270 // zaten yatay/dikey snap'e denk geldiginden burada 7 once test edilir. // 7) Inference: yatay/dikey hizalama. // Once aktif nokta ile (cizim sirasinda), sonra herhangi diskret nokta ile. if (inference) { // Aktif nokta oncelikli hiza kaynagi. Skalar hu = 0, hv = 0; bool dikey = false, yatay = false; Skalar dikeyD = aperture, yatayD = aperture; Skalar dikeyRefU = 0, dikeyRefV = 0, yatayRefU = 0, yatayRefV = 0; auto dene = [&](Skalar ku, Skalar kv) { Skalar du = std::abs(ku - fareU); Skalar dv = std::abs(kv - fareV); if (du < dikeyD) { dikeyD = du; dikey = true; dikeyRefU = ku; dikeyRefV = kv; } if (dv < yatayD) { yatayD = dv; yatay = true; yatayRefU = ku; yatayRefV = kv; } }; if (aktifU && aktifV) dene(*aktifU, *aktifV); for (const auto& a : adaylar) { if (a.tip == SnapTip::OrtaNokta && (dikey || yatay)) continue; dene(a.u, a.v); } if (dikey || yatay) { hu = dikey ? dikeyRefU : fareU; hv = yatay ? yatayRefV : fareV; r.u = dikey ? dikeyRefU : fareU; r.v = yatay ? yatayRefV : fareV; r.dikeyHiza = dikey; r.yatayHiza = yatay; // Inference cizgisi referansi: dikey oncelikli, yoksa yatay. r.hizaU = dikey ? dikeyRefU : yatayRefU; r.hizaV = dikey ? dikeyRefV : yatayRefV; (void)hu; (void)hv; r.tip = dikey ? SnapTip::Dikey : SnapTip::Yatay; return r; } } // 8b) Aci snap (aktif nokta varsa, yatay/dikey yakalayamadiysa). if (aciSnapDeg > 0 && aktifU && aktifV) { Skalar du = fareU - *aktifU, dv = fareV - *aktifV; Skalar uzun = std::sqrt(du * du + dv * dv); if (uzun > 1e-9) { Skalar aci = std::atan2(dv, du); Skalar adimRad = aciSnapDeg * kPi / 180.0; Skalar yuvarlak = std::round(aci / adimRad) * adimRad; r.u = *aktifU + std::cos(yuvarlak) * uzun; r.v = *aktifV + std::sin(yuvarlak) * uzun; r.tip = SnapTip::Aci; r.hizaU = *aktifU; r.hizaV = *aktifV; return r; } } // 9) Izgara. if (izgaraSnap && gridAdim > 0) { r.u = std::round(fareU / gridAdim) * gridAdim; r.v = std::round(fareV / gridAdim) * gridAdim; r.tip = SnapTip::Izgara; return r; } // 99) Serbest. r.tip = SnapTip::Serbest; r.u = fareU; r.v = fareV; return r; } } // namespace fiko::eskiz