#include "fiko/pafta/pafta_kesit.hpp" #include #include #include namespace fiko::pafta { namespace { Vec3d topla3(Vec3d a, Vec3d b) { return {a.x + b.x, a.y + b.y, a.z + b.z}; } Vec3d olcek3(Vec3d a, f64 k) { return {a.x * k, a.y * k, a.z * k}; } } // namespace Vec3d yereldenDunyaya(const GorunumCizim& anaCizim, geometri::Gorunum anaYon, Olcek anaOlcek, Vec2d yerel) { const f64 o = anaOlcek.oran() > 0 ? anaOlcek.oran() : 1.0; // Yerel → ham izdüşüm uzayı (gorunumUret'in tersi), sonra 3B eksen takımı. const Vec2d ham{anaCizim.hamMerkez.x + yerel.x / o, anaCizim.hamMerkez.y + yerel.y / o}; const geometri::GorunumEksen e = geometri::gorunumEksenleri(anaYon); return topla3(e.kok, topla3(olcek3(e.xEks, ham.x), olcek3(e.yEks, ham.y))); } KesitDuzlem kesitDuzlemi(const GorunumCizim& anaCizim, geometri::Gorunum anaYon, Olcek anaOlcek, Vec2d a, Vec2d b) { KesitDuzlem k; const Vec2d d{b.x - a.x, b.y - a.y}; const f64 uz = std::hypot(d.x, d.y); if (uz < 1e-9) return k; k.nokta = yereldenDunyaya(anaCizim, anaYon, anaOlcek, {(a.x + b.x) * 0.5, (a.y + b.y) * 0.5}); // Normal: kesit çizgisine DİK ama görünüm düzlemi İÇİNDE (bakış yönü değil). const geometri::GorunumEksen e = geometri::gorunumEksenleri(anaYon); const Vec2d n2{-d.y / uz, d.x / uz}; k.normal = topla3(olcek3(e.xEks, n2.x), olcek3(e.yEks, n2.y)); k.gecerli = true; return k; } geometri::Gorunum kesitGorunumYonu(geometri::Gorunum anaYon, Vec2d a, Vec2d b) { const Vec2d d{b.x - a.x, b.y - a.y}; const bool yatay = std::abs(d.x) >= std::abs(d.y); using G = geometri::Gorunum; // Yatay kesit çizgisi → yukarıdan/aşağıdan bakılır; dikey çizgi → yandan. switch (anaYon) { case G::On: case G::Arka: return yatay ? G::Ust : G::Sol; case G::Ust: case G::Alt: return yatay ? G::On : G::Sol; case G::Sol: case G::Sag: return yatay ? G::Ust : G::On; default: return G::On; } } std::string kesitEtiketi(char harf) { return std::string(1, harf) + "-" + std::string(1, harf); } char sonrakiKesitHarfi(const Pafta& p) { bool kullanildi[26] = {}; for (const Gorunum& g : p.gorunumler()) if (g.kesitMi && g.kesitHarf >= 'A' && g.kesitHarf <= 'Z') kullanildi[g.kesitHarf - 'A'] = true; for (int i = 0; i < 26; ++i) if (!kullanildi[i]) return (char)('A' + i); return 'Z'; } char sonrakiDetayHarfi(const Pafta& p) { bool kullanildi[26] = {}; for (const Gorunum& g : p.gorunumler()) if (g.detayMi && g.detayHarf >= 'A' && g.detayHarf <= 'Z') kullanildi[g.detayHarf - 'A'] = true; for (int i = 0; i < 26; ++i) if (!kullanildi[i]) return (char)('A' + i); return 'Z'; } GorunumCizim detayCizimUret(const GorunumCizim& anaCizim, Olcek anaOlcek, Vec2d merkez, f64 yaricap, Olcek detayOlcek) { GorunumCizim c; if (!anaCizim.gecerli || !(yaricap > 1e-9)) return c; const f64 aOran = anaOlcek.oran() > 0 ? anaOlcek.oran() : 1.0; const f64 dOran = detayOlcek.oran() > 0 ? detayOlcek.oran() : 1.0; const f64 buyutme = dOran / aOran; const f64 r2 = yaricap * yaricap; const auto icinde = [&](Vec2d p2) { const f64 dx = p2.x - merkez.x, dy = p2.y - merkez.y; return dx * dx + dy * dy <= r2; }; // Segment-daire kesişim parametreleri (t artan sırada; yalnız [0,1] içi). const auto kesisimler = [&](Vec2d a, Vec2d b, f64 t[2]) -> int { const f64 dx = b.x - a.x, dy = b.y - a.y; const f64 fx = a.x - merkez.x, fy = a.y - merkez.y; const f64 A = dx * dx + dy * dy; if (A < 1e-18) return 0; const f64 B = 2 * (fx * dx + fy * dy); const f64 C = fx * fx + fy * fy - r2; const f64 disk = B * B - 4 * A * C; if (disk < 0) return 0; const f64 kokD = std::sqrt(disk); int n = 0; for (f64 tt : {(-B - kokD) / (2 * A), (-B + kokD) / (2 * A)}) if (tt > 1e-12 && tt < 1.0 - 1e-12) t[n++] = tt; return n; }; const auto donustur = [&](Vec2d p2) { return Vec2d{(p2.x - merkez.x) * buyutme, (p2.y - merkez.y) * buyutme}; }; const auto kirp = [&](const std::vector>& kaynak, std::vector>& hedef) { for (const auto& pl : kaynak) { std::vector parca; const auto kapat = [&] { if (parca.size() >= 2) hedef.push_back(std::move(parca)); parca.clear(); }; for (size_t i = 0; i + 1 < pl.size(); ++i) { const Vec2d a = pl[i], b = pl[i + 1]; const bool ai = icinde(a), bi = icinde(b); f64 t[2]; const int n = kesisimler(a, b, t); const auto ara = [&](f64 tt) { return Vec2d{a.x + (b.x - a.x) * tt, a.y + (b.y - a.y) * tt}; }; if (ai && bi) { // tamamen içeride if (parca.empty()) parca.push_back(donustur(a)); parca.push_back(donustur(b)); } else if (ai && !bi) { // içeriden çıkıyor if (parca.empty()) parca.push_back(donustur(a)); if (n >= 1) parca.push_back(donustur(ara(t[0]))); kapat(); } else if (!ai && bi) { // dışarıdan giriyor kapat(); if (n >= 1) parca.push_back(donustur(ara(t[0]))); parca.push_back(donustur(b)); } else if (n == 2) { // dışarıdan geçip iki noktada kesiyor kapat(); parca.push_back(donustur(ara(t[0]))); parca.push_back(donustur(ara(t[1]))); kapat(); } } kapat(); } }; kirp(anaCizim.gorunur, c.gorunur); kirp(anaCizim.gizli, c.gizli); c.boyut = {2 * yaricap * buyutme, 2 * yaricap * buyutme}; c.hamMerkez = anaCizim.hamMerkez; // bilgi amaçlı (detayda yerel uzay merkez=daire merkezi) c.gecerli = !c.gorunur.empty() || !c.gizli.empty(); c.imza = anaCizim.imza; return c; } std::vector> taramaUret(const std::vector>& konturlar, f64 aciDer, f64 aralik) { std::vector> sonuc; if (konturlar.empty() || !(aralik > 1e-9)) return sonuc; const f64 aci = aciDer * std::numbers::pi / 180.0; const f64 ca = std::cos(aci), sa = std::sin(aci); // Tarama, çizgi doğrultusunun DÖNDÜRÜLMÜŞ çerçevesinde yatay taramaya indirgenir: // noktaları -aci döndür, yatay çizgilerle kes, kesişimleri geri döndür. const auto don = [&](Vec2d p) { return Vec2d{p.x * ca + p.y * sa, -p.x * sa + p.y * ca}; }; const auto tersDon = [&](Vec2d p) { return Vec2d{p.x * ca - p.y * sa, p.x * sa + p.y * ca}; }; f64 minY = 1e300, maksY = -1e300; std::vector> donmus; donmus.reserve(konturlar.size()); for (const auto& k : konturlar) { if (k.size() < 3) continue; std::vector d; d.reserve(k.size()); for (const Vec2d& p : k) { const Vec2d q = don(p); minY = std::min(minY, q.y); maksY = std::max(maksY, q.y); d.push_back(q); } donmus.push_back(std::move(d)); } if (donmus.empty() || minY > maksY) return sonuc; // Tarama çizgileri 0'dan başlayan sabit ızgaraya oturur → görünüm taşınınca // desen kaymaz (aynı parçanın iki kesiti aynı ritimde taranır). const f64 ilk = std::ceil(minY / aralik) * aralik; std::vector kesisim; for (f64 y = ilk; y <= maksY; y += aralik) { kesisim.clear(); for (const auto& k : donmus) { const size_t n = k.size(); for (size_t i = 0; i < n; ++i) { const Vec2d& p1 = k[i]; const Vec2d& p2 = k[(i + 1) % n]; if ((p1.y <= y && p2.y > y) || (p2.y <= y && p1.y > y)) { // yarı-açık aralık const f64 t = (y - p1.y) / (p2.y - p1.y); kesisim.push_back(p1.x + t * (p2.x - p1.x)); } } } if (kesisim.size() < 2) continue; std::sort(kesisim.begin(), kesisim.end()); for (size_t i = 0; i + 1 < kesisim.size(); i += 2) { // tek-çift kuralı if (kesisim[i + 1] - kesisim[i] < 1e-9) continue; sonuc.push_back({tersDon({kesisim[i], y}), tersDon({kesisim[i + 1], y})}); } } return sonuc; } } // namespace fiko::pafta