#include #include #include "fiko/render/referans_olcek.hpp" #include "fiko/sahne/alan_secim.hpp" #include "fiko_test.hpp" using namespace fiko; namespace { // Bilinen ortografik VP: dunya [-10,10] → NDC [-1,1]; viewport 0..100 piksel. // Y ekseni dikey()'de ters (ekran asagi +) → dunya +y = ekran ust (kucuk y). Mat4f testVp() { return Mat4f::dikey(-10, 10, -10, 10, -10, 10); // gorunum = birim } const Vec2f VPMIN{0.0f, 0.0f}; const Vec2f VPBOYUT{100.0f, 100.0f}; Vec2f proj(const Vec3f& w) { Vec2f e{}; ekranProjekte(testVp(), w, VPMIN, VPBOYUT, e); return e; } } // namespace // (1) Ekran projeksiyon matematigi: bilinen dunya noktalari → dogru ekran pikseli. FIKO_TEST(alan_ekran_projeksiyon) { const Vec2f mrk = proj({0, 0, 0}); FIKO_YAKIN(mrk.x, 50.0f, 1e-3f); FIKO_YAKIN(mrk.y, 50.0f, 1e-3f); // dunya x=+10 → ekran x=100 (sag kenar) const Vec2f sag = proj({10, 0, 0}); FIKO_YAKIN(sag.x, 100.0f, 1e-3f); // dunya y=+10 → dikey() Y-flip → ekran y=0 (ust) const Vec2f ust = proj({0, 10, 0}); FIKO_YAKIN(ust.y, 0.0f, 1e-3f); } // (2) Nokta-icinde-dikdortgen: sinir dahil, dis haric. FIKO_TEST(alan_nokta_dikdortgende) { const Vec2f asg{10, 10}, azm{90, 90}; FIKO_KONTROL(noktaDikdortgende({50, 50}, asg, azm)); FIKO_KONTROL(noktaDikdortgende({10, 10}, asg, azm)); // sinir dahil FIKO_KONTROL(!noktaDikdortgende({5, 50}, asg, azm)); // sol dis FIKO_KONTROL(!noktaDikdortgende({50, 95}, asg, azm)); // alt dis } // (3) Nokta-icinde-poligon (even-odd): konveks + konkav. FIKO_TEST(alan_nokta_poligonda) { // Kare (konveks) const std::vector kare{{0, 0}, {10, 0}, {10, 10}, {0, 10}}; FIKO_KONTROL(noktaPoligonda({5, 5}, kare)); FIKO_KONTROL(!noktaPoligonda({15, 5}, kare)); // Konkav "C" harfi benzeri: cukur bolge dista sayilmali. const std::vector konkav{{0, 0}, {10, 0}, {10, 4}, {4, 4}, {4, 6}, {10, 6}, {10, 10}, {0, 10}}; FIKO_KONTROL(noktaPoligonda({2, 5}, konkav)); // sol govde ic FIKO_KONTROL(!noktaPoligonda({7, 5}, konkav)); // cukur (dis) } // (4) AABB-dikdortgen kesisimi (kesen/dis on-elemesi). FIKO_TEST(alan_aabb_kesisim) { const Vec2f rAsg{20, 20}, rAzm{60, 60}; // Tamamen icinde FIKO_KONTROL(aabbDikdortgende({30, 30}, {40, 40}, rAsg, rAzm)); // Kismen tasan (kesen) FIKO_KONTROL(aabbDikdortgende({50, 50}, {80, 80}, rAsg, rAzm)); // Tamamen disinda FIKO_KONTROL(!aabbDikdortgende({70, 70}, {90, 90}, rAsg, rAzm)); } // (5) Firca izi segment-AABB kesisimi. FIKO_TEST(alan_firca_segment_kesisim) { const Vec2f asg{40, 40}, azm{60, 60}; // Kutuyu ortasindan gecen yatay segment FIKO_KONTROL(segmentAabbKesiyor({0, 50}, {100, 50}, asg, azm)); // Kutuya degmeyen segment FIKO_KONTROL(!segmentAabbKesiyor({0, 0}, {30, 30}, asg, azm)); // Uc kutu icinde FIKO_KONTROL(segmentAabbKesiyor({50, 50}, {90, 90}, asg, azm)); } // (6) Containment vs kesen: 8-kose kutu bolgeye kismen tasarsa "tamamen icinde" // yanlis, "kesen" dogru; tamamen icinde/disinda ayrimi. FIKO_TEST(alan_containment_vs_kesen) { // Bir kutunun 8 kosesi ekran-uzayinda (25..75 karesi) std::vector kutu; for (int i = 0; i < 4; ++i) kutu.push_back({(i & 1) ? 75.0f : 25.0f, (i & 2) ? 75.0f : 25.0f}); auto hepIcinde = [&](const Vec2f& asg, const Vec2f& azm) { for (const Vec2f& p : kutu) if (!noktaDikdortgende(p, asg, azm)) return false; return true; }; auto herhangiIcinde = [&](const Vec2f& asg, const Vec2f& azm) { for (const Vec2f& p : kutu) if (noktaDikdortgende(p, asg, azm)) return true; return false; }; // Bolge kutuyu tamamen kapsar → tamamen icinde FIKO_KONTROL(hepIcinde({0, 0}, {100, 100})); // Bolge kutuyu kismen keser → tamamen icinde DEGIL, kesen DOGRU FIKO_KONTROL(!hepIcinde({50, 50}, {100, 100})); FIKO_KONTROL(herhangiIcinde({50, 50}, {100, 100})); // Bolge kutunun tamamen disinda → kesen bile DEGIL (dis-trap true olur) FIKO_KONTROL(!herhangiIcinde({80, 80}, {95, 95})); } // (7) Segment-poligon kesisimi (poligon/firca izi gercek kenar testi). FIKO_TEST(alan_segment_poligon_kesisim) { const std::vector ucgen{{0, 0}, {100, 0}, {50, 100}}; // Segment ucgeni ortadan gecer FIKO_KONTROL(segmentPoligonKesiyor({0, 50}, {100, 50}, ucgen)); // Segment ucgene hic degmez (ustunden gecer) FIKO_KONTROL(!segmentPoligonKesiyor({0, 90}, {30, 90}, ucgen)); // Bir uc ucgen icinde FIKO_KONTROL(segmentPoligonKesiyor({50, 30}, {200, 30}, ucgen)); } // (8) GORUNMEZ KUTU HATASI regresyonu — KESEN dikdortgen: govde ekran-AABB // kosesi bolgede AMA gercek mesh (ucgen kenarlari) bolgeye degmiyor → SEÇME. // Kok neden buydu: eskiden bbox-AABB "degdi→kesen" derdi. Simdi ucgen kenar // segmenti dikdortgeni kesmedikce ve hicbir mesh vertexi icinde olmadikca kesmez. FIKO_TEST(alan_kesen_bbox_kosesi_ama_mesh_degil) { // Sol-alt kosesi bos ("L"/ucgen) bir mesh. Ekran-uzayi ucgen: bbox [0..100]^2 // ama gercek ucgen sag-ust yarim. Kose (0,100) bbox'ta ama mesh disinda. const std::vector mv{{100, 0}, {100, 100}, {0, 100}}; // ucgen vertexleri const std::vector> ke{{0, 1}, {1, 2}, {2, 0}}; // Kucuk kesen-dikdortgen SOL-ALT bosluga yerlesir: (5,5)-(30,30). // Bu bolge mesh bbox'inin (0..100) kosesinde ama gercek ucgenin DISINDA. const Vec2f rAsg{5, 5}, rAzm{30, 30}; auto vertexIcinde = [&]() { for (const Vec2f& p : mv) if (noktaDikdortgende(p, rAsg, rAzm)) return true; return false; }; auto kenarKesiyor = [&]() { for (const auto& e : ke) if (segmentAabbKesiyor(mv[e.first], mv[e.second], rAsg, rAzm)) return true; return false; }; // GERCEK GEOMETRI: ne vertex icinde ne kenar kesiyor → SEÇILMEZ (dogru). FIKO_KONTROL(!vertexIcinde()); FIKO_KONTROL(!kenarKesiyor()); // ESKI HATALI YOL ISPATI: bbox-AABB testi YANLIS "kesiyor" derdi. const Vec2f gAsg{0, 0}, gAzm{100, 100}; // govde ekran-AABB (bbox 8 kose) FIKO_KONTROL(aabbDikdortgende(gAsg, gAzm, rAsg, rAzm)); // eski: yanlis pozitif } // (9) GORUNMEZ KUTU HATASI regresyonu — FIRÇA IZI: iz govde bbox'indan geciyor // ama gercek mesh kenarlarina degmiyor → SEÇME. (Kullanicinin canli bildirdigi.) FIKO_TEST(alan_firca_bbox_geciyor_ama_mesh_degil) { // Ayni sag-ust ucgen mesh. const std::vector mv{{100, 0}, {100, 100}, {0, 100}}; const std::vector> ke{{0, 1}, {1, 2}, {2, 0}}; // Firca izi sol-alt bosluktan gecer (bbox icinde, ucgen disinda). const std::vector iz{{2, 40}, {40, 2}}; auto vertexIcinde = [&]() { // firca = poligon-ic testi (kapali degil ama izde nokta) for (const Vec2f& p : mv) if (noktaPoligonda(p, iz)) return true; return false; }; auto izKenariKesiyor = [&]() { for (const auto& e : ke) for (std::size_t i = 1; i < iz.size(); ++i) // segmentKesisiyor iz segmenti vs mesh kenari (kumeTest ile ayni mantik) if (segmentPoligonKesiyor(mv[e.first], mv[e.second], std::vector{iz[i - 1], iz[i]})) return true; return false; }; FIKO_KONTROL(!vertexIcinde()); FIKO_KONTROL(!izKenariKesiyor()); // Kanit: bbox-AABB'sini iz KESER (eski hatali yol yanlis pozitif verirdi). FIKO_KONTROL(segmentAabbKesiyor(iz[0], iz[1], {0, 0}, {100, 100})); } // (10) DOGRU POZITIF kontrol: iz gercekten ucgen govdesini keserse SEÇILMELI. FIKO_TEST(alan_firca_gercek_kesim_secilir) { const std::vector mv{{100, 0}, {100, 100}, {0, 100}}; const std::vector> ke{{0, 1}, {1, 2}, {2, 0}}; const std::vector iz{{40, 40}, {90, 90}}; // ucgen govdesinden gecer bool kesti = false; for (const auto& e : ke) for (std::size_t i = 1; i < iz.size(); ++i) if (segmentPoligonKesiyor(mv[e.first], mv[e.second], std::vector{iz[i - 1], iz[i]})) kesti = true; // Ya vertex icinde ya kenar kesim; en az biri true → secilir. bool vertexIc = false; for (const Vec2f& p : mv) if (noktaPoligonda(p, std::vector{{40, 40}, {90, 40}, {90, 90}, {40, 90}})) vertexIc = true; FIKO_KONTROL(kesti || vertexIc); } // (11) EKSIK SEÇIM regresyonu — İçinde-trap: GORSEL tamamen icerideki kati // SEÇILMELI. Eski kod 3B bbox'in 8 projekte kosesini sart kosuyordu; dondurulmus/ // kavisli govdede bbox kosesi mesh siluetinin DISINA duser → gorsel-iceride nesne // atlanirdi. Yeni kod GERCEK mesh vertexleri test eder. (Kure/dondurulmus kup // senaryosu: mesh ekran-izdusumu daire, bbox koseleri daire disinda.) FIKO_TEST(alan_icinde_gorsel_iceride_bbox_disari) { // Projekte mesh vertexleri: (50,50) merkezli yaricap 40 sekizgen (kure izdusumu). std::vector mesh; for (int i = 0; i < 8; ++i) { const f32 a = (f32)i * 0.785398f; // 45 derece adim mesh.push_back({50.0f + 40.0f * std::cos(a), 50.0f + 40.0f * std::sin(a)}); } // Projekte 3B-bbox koseleri (45 derece donmus kup): merkezden 56.6 uzakta. const std::vector bboxKose{{50, 106.6f}, {106.6f, 50}, {50, -6.6f}, {-6.6f, 50}}; const Vec2f rAsg{5, 5}, rAzm{95, 95}; // trap: mesh'i gorsel tamamen kapsar auto hepIcinde = [&](const std::vector& pts) { for (const Vec2f& p : pts) if (!noktaDikdortgende(p, rAsg, rAzm)) return false; return true; }; // YENI (gercek geometri): tum mesh vertexleri icinde → SEÇILIR. FIKO_KONTROL(hepIcinde(mesh)); // ESKI HATALI YOL ISPATI: bbox kosesi disarida → yanlis "seçme" karari verirdi. FIKO_KONTROL(!hepIcinde(bboxKose)); } // (12) EKSIK SEÇIM regresyonu — ekran-sabit DATUM EKSEN: gorsel cizgi kare icinde // → SEÇILMELI. referansGorselMvp olcekli projeksiyon (render/GPU-pick ile ayni // kaynak) trap testinde de kullanilmali; dunya-olcek projeksiyon cizgiyi ekran // disina tasirip nesneyi atlatirdi (gorsel kapsama == secim ihlali). FIKO_TEST(alan_datum_ekran_sabit_icinde) { // Eksen cizgisi yerel (0,0,0)-(0,8,0); kamera baz mesafede (olcek=0.224 eksen). const Mat4f dVp = referansGorselMvp(testVp(), Mat4f::birim(), {0, 0, 0}, REFERANS_BAZ_MESAFE, REFERANS_ORAN_EKSEN, 1.0f); Vec2f ucOlcekli{}, ucDunya{}; FIKO_KONTROL(ekranProjekte(dVp, {0, 8, 0}, VPMIN, VPBOYUT, ucOlcekli)); FIKO_KONTROL(ekranProjekte(testVp(), {0, 8, 0}, VPMIN, VPBOYUT, ucDunya)); const Vec2f rAsg{30, 30}, rAzm{70, 70}; // gorsel cizgiyi kapsayan kare // OLCEKLI (gorselle ayni): her iki uc da kare icinde → İçinde-trap SEÇER. FIKO_KONTROL(noktaDikdortgende(ucOlcekli, rAsg, rAzm)); Vec2f orij{}; FIKO_KONTROL(ekranProjekte(dVp, {0, 0, 0}, VPMIN, VPBOYUT, orij)); FIKO_KONTROL(noktaDikdortgende(orij, rAsg, rAzm)); // ESKI HATALI YOL ISPATI: dunya-olcek uc kare DISINDA → nesne atlanirdi. FIKO_KONTROL(!noktaDikdortgende(ucDunya, rAsg, rAzm)); } // (13) Konkav poligon "tamamen icinde" duzeltmesi: tum vertexler poligon icinde // AMA bir mesh kenari cukurdan disari tasiyor → CATIA kurali: tamamen-icinde // DEGIL (gorsel kapsama == secim; tasan kismin pikselleri poligon disinda). FIKO_TEST(alan_poligon_konkav_kenar_tasma) { // "C" poligon (test 3 ile ayni): cukur x∈[4,10], y∈[4,6] DIS bolge. const std::vector pg{{0, 0}, {10, 0}, {10, 4}, {4, 4}, {4, 6}, {10, 6}, {10, 10}, {0, 10}}; // Mesh kenari (8,2)-(8,8): iki ucu da icerde (alt/ust kol) ama cukuru deler. const Vec2f a{8, 2}, b{8, 8}; FIKO_KONTROL(noktaPoligonda(a, pg)); FIKO_KONTROL(noktaPoligonda(b, pg)); // Kenar poligon sinirini kesiyor (kumeTest tamIcinde-düzeltmesiyle ayni mantik). bool kesti = false; const std::size_t n = pg.size(); for (std::size_t i = 0, j = n - 1; i < n && !kesti; j = i++) if (segmentKesisiyor(a, b, pg[j], pg[i])) kesti = true; FIKO_KONTROL(kesti); // → tamIcinde=false olmali } // (14) CATIA Eksen Sistemi alt-ordinal semantigi: 1..3 eksen, 4..6 L-kose düzlem, // 7 orijin nokta. sketchDuzlemBul + render/pick bu sabitleri paylasir → sozlesme // kilitli (kayarsa düzlem-seçimi/sketch kaynagi kirilir). FIKO_TEST(eksensis_alt_ordinal_semantik) { using namespace eksensis; FIKO_KONTROL(EKSEN_X == 1 && EKSEN_Y == 2 && EKSEN_Z == 3); FIKO_KONTROL(DUZLEM_XY == 4 && DUZLEM_YZ == 5 && DUZLEM_ZX == 6); FIKO_KONTROL(ORIJIN == 7); // eksenMi: yalniz 1..3 FIKO_KONTROL(eksenMi(EKSEN_X) && eksenMi(EKSEN_Z)); FIKO_KONTROL(!eksenMi(DUZLEM_XY) && !eksenMi(ORIJIN) && !eksenMi(0)); // duzlemMi: yalniz 4..6 (sketchDuzlemBul bu araligi DÜZLEM kaynagina cozer) FIKO_KONTROL(duzlemMi(DUZLEM_XY) && duzlemMi(DUZLEM_ZX)); FIKO_KONTROL(!duzlemMi(EKSEN_X) && !duzlemMi(ORIJIN) && !duzlemMi(0)); }