T: tema · Z: kodu büyüt · S: konuşmacı notları · F: tam ekran · Esc: genel bakış · ⌂ ders haritası
OYUN GEOMETRİSİ VE FİZİĞİ · G1
Geometri Temelleri
Yön testi, kesişim, nokta çokgenin içinde mi, dışbükey zarf — her oyunun görünmeyen cetveli
PRESS ▶ TO START

Bu derste oyunun haritası 🗺️

  1. Yön testi — tek bir çapraz çarpım; DOOM’un duvar testi, Mario 64’ün zemin testi
  2. Kesişim — iki doğru parçası; “nerede?” için t; DOOM’un mermisi nasıl isabet eder
  3. Nokta çokgenin içinde mi? — ışın atma kuralı ve köşe tuzağı; Paradox’un renkli haritası
  4. Alan ve dönüş yönü — ayakkabı bağı formülü; arka yüz ayıklama
  5. Dışbükey zarf — sırala + yığın (monoton zincir); çarpışma şekillerinin kaynağı
  6. Her adımda — elle izleme → canlı demo → kod (JavaScript / Python / C#) → oyunlardaki karşılığı
🎮 Bu dersin sonunda…

bir merminin duvara nerede çarptığını, tıklanan noktanın hangi bölgede olduğunu ve bir nokta bulutunun dış sınırını açı, sinüs kullanmadan, yalnızca çarpma ve çıkarmayla hesaplayabilecek; 1993’ün DOOM’unda ve 1996’nın Mario 64’ünde aynı satırları tanıyacaksınız.

🤔 ISINMA: HARİTADA TIKLADIĞIM YER HANGİ ÜLKE?

Bir strateji oyununda haritaya tıklıyorsunuz ve oyun “Anadolu” bölgesini seçiyor. Bölgenin sınırı yüzlerce köşeli, girintili çıkıntılı bir çokgen. Oyun, tıkladığınız noktanın o çokgenin içinde olduğunu nasıl anlıyor?

  • “Merkezine uzaklığa bakarım” → çalışmaz: uzun, kıvrık bir bölgenin merkezi dışarıda bile kalabilir.
  • “Bütün pikselleri önceden boyarım” → çalışır! Bazı oyunlar tam olarak bunu yapar (Level 2’de).
  • Geometrik cevap şaşırtıcı derecede basit: noktadan bir yöne ışın gönder, sınırı kaç kez kestiğini say.
  • Bu sunumdaki her şey tek bir küçük sorunun tekrarı: “Bu nokta, şu doğrunun hangi tarafında?”

🎯 Bu konu için bilmeniz gerekenler

Yeşil = gerekli · kesikli = faydalı ama şart değil. Tıklayınca ilgili sunuma gider.

Neden?Y0’da çapraz çarpımı “hedef solumda mı, sağımda mı?” için kullanmıştık. Bu sunum o tek satırı alıp dört ayrı algoritmaya dönüştürüyor. Dışbükey zarf ise iki eski tanıdığı birleştiriyor: sıralama ve yığın.
🧰 Kodlamadan korkuyorsanızBütün sunumda trigonometri yok. Bilmeniz gereken tek formül iki çarpma ve bir çıkarma; gerisi if ve döngü. Her kod parçasından sonra “kodu okuyalım” slaydı var.

Mini sözlük

orientation test · CCWyön testi · saat yönünün tersiÜç noktanın sola mı, sağa mı döndüğünü söyleyen işaret
cross productçapraz çarpım2B’de tek sayı: a.x·b.y − a.y·b.x; işareti yönü, büyüklüğü alanı verir
collineardoğrusalÜç noktanın aynı doğru üzerinde olması (çapraz = 0)
line segment · parametric formdoğru parçası · parametrik gösterimİki uçlu doğru · A + t·(B − A), t: 0’dan 1’e
point in polygon · ray castingnokta çokgende · ışın atmaBir noktanın kapalı şeklin içinde olup olmadığı · ışın gönderip kesişim sayma
convex · concavedışbükey · içbükeyGirintisi olmayan şekil · girintisi olan şekil
convex hulldışbükey zarfBir nokta kümesini saran en küçük dışbükey çokgen (lastik bant)
winding order · back-face cullingdönüş yönü · arka yüz ayıklamaKöşelerin saat yönünde mi sıralandığı · sırtı dönük üçgeni çizmemek
epsilon · robustnesseşik payı · sağlamlıkKayan nokta hatasına karşı küçük tolerans · sınır durumlarda yanlış cevap vermeme
LEVEL 0
Yön testi
sol mu, sağ mı, düz mü?

Çizimle: “hangi tarafımda?” sorusu

Taret A’da duruyor ve B yönüne bakıyor. Hedefe dönmek için hangi tarafa çevirmeli?açıyı bilmeye gerek yok: yalnızca “solda mı, sağda mı?”AB (baktığı yön)👾hedef solda → sola dön👾hedef sağda → sağa dönSOL tarafSAĞ taraf

Oyunlardaki pek çok soru aslında bu tek sorudur: bir yön (A’dan B’ye) ve bir nokta (hedef) verilmiş; nokta yönün solunda mı, sağında mı, tam üstünde mi? Bu bölüm, bu soruyu iki çarpma ve bir çıkarmayla cevaplayan yön testini kuruyor.

Problem: hedef hangi tarafımda?

🎮 Oyunlarda her kare sorulan soru
  • Taret: hedefe dönmek için sola mı, sağa mı çevirmeliyim?
  • Yarış: araba pistin orta çizgisinin hangi tarafında? Ters yöne mi gidiyor?
  • Kapı / bitiş çizgisi: oyuncu çizgiyi geçti mi (bu kare bir tarafta, geçen kare öbür tarafta)?
  • Duvar: oyuncu duvarın önünde mi, arkasında mı? (hangi yüzünü çizeceğim?)
  • Dövüş: rakip arkamda mı kaldı, dönmeli miyim?
İlk akla gelen: açı hesapla

atan2 ile A→B’nin açısını ve A→C’nin açısını bul, farkına bak.

  • atan2 pahalı bir fonksiyon; binlerce nesne × 60 kare.
  • Açılar −180° ile +180° arasında sarar: 179° ile −179° arasındaki fark 2° mi, 358° mi? Her seferinde düzeltme kodu.
  • Sonuç ondalıklı: tam “doğrunun üstünde” durumu güvenilmez.

Oysa açının kendisi gerekmiyor; yalnızca hangi taraf olduğu gerekiyor.

Fikir: üçgenin alanının işareti

A, B, C’den iki vektör kur: u = B − A ve v = C − A. Bunların çapraz çarpımı:

$$u \times v = u_x v_y - u_y v_x$$

$$= (B_x - A_x)(C_y - A_y) - (B_y - A_y)(C_x - A_x)$$

  • Bu sayının büyüklüğü: u ve v’nin gerdiği paralelkenarın alanı (= ABC üçgeninin alanının 2 katı).
  • Bu sayının işareti: v’ye ulaşmak için u’yu hangi yöne çevirmeniz gerektiği. Yani C’nin hangi tarafta olduğu.
  • Sıfır: alan yok → üç nokta aynı doğru üzerinde.
  • İki çarpma, beş çıkarma. Bölme yok, karekök yok, trigonometri yok. Koordinatlar tam sayıysa sonuç hatasız.
⚠️ Ekranda y aşağı doğru!

Matematik kitabında (y yukarı) pozitif = sola dönüş (saat yönünün tersi).

Ekran koordinatında (p5.js, çoğu 2B motor) y aşağı arttığı için aynı sayı sağa dönüş demek.

Ezberlemeyin: bildiğiniz bir üçgenle bir kez deneyin, işareti not edin. Unity’nin dünya koordinatında y yukarıdır; Unity’nin arayüz (UI) ve p5.js koordinatında aşağı.

Nokta çarpımla farkı (Y0)Nokta çarpım: “önümde mi, arkamda mı?” · Çapraz çarpım: “solumda mı, sağımda mı?” İkisi birlikte hedefin tam yönünü verir.

Çizimle: işaret tarafı, büyüklük alanı söyler

A (1, 1)B (5, 3)C = (3, 5): +12 → SOLDAalan 6C = (4, 1): −6 → SAĞDAalan 3C = (7, 4): 0 → doğrunun üstünde(9, 5): 0

Mavi ok A’dan B’ye. Yeşil üçgenin sayısı artı (C solda), kırmızı üçgenin sayısı eksi (C sağda); sayının yarısı üçgenin alanı. Doğrunun üstündeki gri noktalarda üçgen yok, alan 0. Sonraki slayt bu dört noktanın hesabını adım adım yapıyor.

Elle hesap: üç örnek

A = (1, 1), B = (5, 3). Yani u = B − A = (4, 2). (Matematik koordinatı: y yukarı.)

Cv = C − Aux·vy − uy·vxsonuçanlamı
(3, 5)(2, 4)4·4 − 2·2+12C solda; alan = 6
(4, 1)(3, 0)4·0 − 2·3−6C sağda; alan = 3
(7, 4)(6, 3)4·3 − 2·60A, B, C aynı doğruda
(9, 5)(8, 4)4·4 − 2·80aynı doğrunun devamında

Son iki satır: C’yi doğru boyunca kaydırmak sonucu değiştirmiyor. Doğruya paralel kaydırmak da (ör. (3, 5) → (5, 6)) değiştirmez: 4·5 − 2·4 = 12. Çünkü üçgenin tabanı ve yüksekliği aynı kalır.

Siz hesaplayın

A = (0, 0), B = (3, 0), C = (1, −2). Sonuç?

u = (3, 0), v = (1, −2): 3·(−2) − 0·1 = −6. Negatif: matematik koordinatında C sağda (aşağıda). Aynı sayılar ekran koordinatı olsaydı C yukarıda görünürdü ve yine “−6” çıkardı: formül değişmez, yorum değişir.

Canlı: yön testi 🧭

  1. Ekranı okuyun: siyah ok A→B; kesikli çizgi o okun sonsuz uzantısı. Düzlem ikiye ayrılıyor: mavi yarı (çapraz > 0) ve turuncu yarı (çapraz < 0). Altta formül, sayılar yerine konmuş hâliyle.
  2. C’yi kesikli çizginin üzerinden karşıya geçirin: alttaki sayının işareti o anda değişir.
  3. C’yi çizgiye paralel kaydırın: sayı hiç değişmiyor (az önceki tablonun son satırları).
  4. C’yi çizgiden uzaklaştırın: sayı büyür. Sayı, “ne kadar yanda” sorusunun da kaba bir ölçüsü.
  5. A ile B’nin yerini değiştirin (B’yi A’nın soluna sürükleyin): renkler yer değiştirir. Yön testi yöne bağlıdır: A→B ile B→A zıt cevap verir.
🔎 DEMODA DENEYİN
  1. Ekranı okuyun: siyah ok A→B; kesikli çizgi o okun sonsuz uzantısı. Düzlem ikiye ayrılıyor: mavi yarı (çapraz > 0) ve turuncu yarı (çapraz < 0). Altta formül, sayılar yerine konmuş hâliyle.
  2. C’yi kesikli çizginin üzerinden karşıya geçirin: alttaki sayının işareti o anda değişir.
  3. C’yi çizgiye paralel kaydırın: sayı hiç değişmiyor (az önceki tablonun son satırları).
  4. C’yi çizgiden uzaklaştırın: sayı büyür. Sayı, “ne kadar yanda” sorusunun da kaba bir ölçüsü.
  5. A ile B’nin yerini değiştirin (B’yi A’nın soluna sürükleyin): renkler yer değiştirir. Yön testi yöne bağlıdır: A→B ile B→A zıt cevap verir.

🔗 Başka gözle: etkileşimli anlatımlar ve yazılar

Aynı konuyu başka bir anlatımla, kendi elinizle oynayarak görmek çok işe yarar. Kartlar yeni sekmede açılır; çoğu İngilizce ama görsel ve etkileşimli.

Kod: yön testi

Bu fonksiyon sunumun geri kalanındaki her algoritmanın içinde çağrılacak. C# sekmesindeki taret örneği: Unity’nin dünya koordinatında y yukarı olduğu için pozitif = sol.

🌱 Isınma kodu: çapraz çarpım

Başlangıç kodu yanlışlıkla nokta çarpımı hesaplıyor (Y0’daki “önümde mi?” testi). Çapraz çarpımda terimler çapraz eşleşir (x ile y) ve aradaki işlem çıkarmadır: abx * acy − aby * acx. Az önceki tablonun dört satırı testlerin arasında.

Oyunlarda: DOOM’un duvar testi (1993)

  • DOOM’un haritası duvar çizgilerinden oluşur. Motor sürekli sorar: “şu nokta bu çizginin önünde mi, arkasında mı?”
  • left ve right bizim formülün iki çarpımı; right < left karşılaştırması, farkın işaretine bakmakla aynı şey: çapraz çarpımın işareti.
  • Bu fonksiyon her şeyin altında: canavar duvara çarptı mı, oyuncu bir kapı çizgisini geçti mi, mermi hangi yüzden girdi…
  • 1993’te kayan noktalı işlem yavaştı; DOOM bütün sayıları sabit noktalı tam sayı (fixed_t) tutar. Yan etkisi: sonuçlar her bilgisayarda birebir aynı (bu sayede oyun kayıtları, “demo”lar, yıllar sonra da aynı oynar).

Kaynak: id Software, DOOM açık kaynak kodu, linuxdoom-1.10/p_maputl.c (kısaltıldı: dikey ve yatay çizgiler için iki kestirme çıkarıldı).

Uygulama: nokta üçgenin içinde mi? (Super Mario 64)

  • Bir nokta üçgenin içindeyse, üç kenarın hepsinin aynı tarafındadır. Üç yön testi; biri bile ters işaretliyse dışarıda.
  • Super Mario 64: dünya, zemin / duvar / tavan üçgenlerinden oluşur. Her kare “Mario’nun ayağının altında hangi zemin üçgeni var?” diye sorulur.
  • İkinci sekmeye bakın: oyunun kodu (yukarıdan bakışta, x ve z ile) üç kez aynı çapraz çarpımı hesaplıyor; biri negatifse continue ile sıradaki üçgene geçiyor.
  • Navmesh’te “ajan hangi üçgende?” sorusu da budur (Y9).
📜 Tam sayının bedeliMario 64 köşe koordinatlarını tam sayıya yuvarlar. Bu yüzden komşu iki üçgenin arasında bir birimlik boşluklar ve üst üste binmeler kalır; hız koşucularının kullandığı birçok hata ve pannenkoek2012’nin ünlü “görünmez duvarlar” videosu bu sınır durumlarının hikâyesidir.

Sağlamlık: “neredeyse sıfır” sorunu

  • Koordinatlar ondalıklıysa çapraz çarpım tam 0 yerine 0.0000001 ya da −0.0000001 çıkabilir. Aynı üç nokta, farklı sırayla verilince farklı cevap.
  • Belirtiler: karakter iki duvarın birleştiği köşeden “sızar”; mermi iki üçgenin tam arasından geçer; navmesh’te ajan bir kenarın üstünde titrer.
  • Çare 1 · tam sayı: ızgara oyunları, DOOM’un sabit noktası. Sonuç hatasız (ama Mario 64’teki gibi yuvarlama boşlukları doğabilir).
  • Çare 2 · eşik payı: abs(çapraz) < ε ise “doğrusal” say. ε, koordinatların büyüklüğüne göre seçilir.
  • Çare 3 · tutarlılık: ortak bir kenarı her yerde aynı uç sırasıyla sına; iki komşu üçgen aynı kenar için aynı cevabı versin.
📜 1996: sağlam yön testi

Jonathan Shewchuk, yön testinin kayan noktalı sayılarla her zaman doğru işaret vermesini sağlayan “uyarlamalı hassas” yöntemleri yayımladı: sonuç sıfıra tehlikeli ölçüde yakınsa hesabı daha yüksek hassasiyetle tekrarlar, değilse hızlı yoldan döner.

Bugün üçgenleme ve geometri kütüphanelerinin çoğu onun kodunu kullanır.

Ders: bu sunumdaki en basit satır, geometri kodunun en çok hata çıkaran satırıdır.

Quiz 1 · yön testi
Bir yarış oyununda pistin orta çizgisi A’dan B’ye gidiyor. Geçen kare cross(A, B, araba) = +350, bu kare −20. Ne oldu?
İşaret değişimi = doğrunun bir tarafından öbür tarafına geçiş. Bitiş çizgisi, kontrol noktası ve “kapıdan geçti mi?” testlerinin hepsi iki ardışık karenin işaretlerini karşılaştırır. Sayının büyüklüğü uzaklıkla orantılıdır ama piksel cinsinden uzaklık değildir (AB’nin uzunluğuna bölmek gerekir).
LEVEL 1
Kesişim
mermi duvara çarptı mı, nerede?

Problem: iki doğru parçası kesişiyor mu?

🎮 Oyunlarda
  • Mermi: bu karede A’dan B’ye gitti; yolu bir duvarı kesti mi?
  • Görüş hattı: gözden hedefe çekilen çizgi bir engeli kesiyor mu? (Y11)
  • Lazer, ayna, ip, kanca: ışın hangi yüzeye, nerede değdi?
  • Seviye editörü: çizilen duvarlar birbirini kesiyor mu?
  • Navmesh: iki nokta arasında “düz git” mümkün mü? (Y9’daki ip germe)
İlk akla gelen: doğru denklemi

Lisedeki gibi iki doğruyu y = m·x + b yaz, ortak çöz.

  • Dikey duvar: eğim m sonsuz; sıfıra bölme. Oyun haritalarının yarısı dikey duvardır!
  • Paralel doğrular: ortak çözüm yok; yine özel durum.
  • Bulunan nokta doğruların kesişimi; parçaların içinde mi diye ayrıca bakmak gerek.
  • Bölme → ondalıklı sayı → sınırda yanlış cevap.

Yön testiyle hiçbir bölme yapmadan “kesişiyor mu?” cevaplanır.

Çizimle: “farklı yanlarda” neden iki kez sorulur?

İki koşul da tutuyorKESİŞİYORA (0, 0)B (6, 4)C (1, 4)D (5, 0)(3, 2)d₁ = +20 · d₂ = −20 · d₃, d₄ zıt işaretliYalnızca ilk koşul tutuyorkesişmiyor (kesişim AB’nin ötesinde)A (0, 0)B (6, 4)C (7, 7)D (9, 3)ilk koşul tutuyor, ikincisi tutmuyor

Solda iki koşul da tutuyor: parçalar kesişiyor. Sağda C ve D, AB doğrusunun farklı yanlarında ama kesişim AB parçasının ötesinde kalıyor; bunu yakalayan ikinci koşuldur (A ve B, CD’nin aynı yanında).

Fikir: ikisi de öbürünü ikiye ayırmalı

  • AB doğrusu düzlemi ikiye böler. CD parçası AB’yi kesiyorsa C ve D farklı taraflarda olmak zorunda.
  • Ama bu yetmez: C ve D farklı taraflarda olup kesişim AB parçasının ötesinde kalabilir.
  • O yüzden simetrik koşul da gerekir: A ve B, CD doğrusunun farklı taraflarında.
  • İki koşul birlikte ↔ parçalar kesişir. Toplam dört yön testi:

$$d_1 = \text{cross}(A,B,C) \qquad d_2 = \text{cross}(A,B,D)$$

$$d_3 = \text{cross}(C,D,A) \qquad d_4 = \text{cross}(C,D,B)$$

Kesişir ⇔ d₁ ve d₂ zıt işaretli ve d₃ ve d₄ zıt işaretli. Kodda: d1 * d2 < 0 && d3 * d4 < 0.

Elle izleme

A = (0, 0), B = (6, 4) · C = (1, 4), D = (5, 0)

testhesapsonuç
d₁ = cross(A,B,C)6·4 − 4·1+20
d₂ = cross(A,B,D)6·0 − 4·5−20
d₃ = cross(C,D,A)4·(−4) − (−4)·(−1)−20
d₄ = cross(C,D,B)4·0 − (−4)·5+20

d₁·d₂ < 0 ✔ ve d₃·d₄ < 0 ✔ → kesişiyor.

D’yi (2, 3) yapın: d₂ = 6·3 − 4·2 = +10. d₁ ile aynı işaret → C ve D aynı tarafta → kesişmiyor. Geri kalan iki teste gerek bile yok.

Özel durumlar: uç değmesi ve aynı doğru

  • Bir yön testi tam 0 çıkarsa o uç, öbür parçanın doğrusu üzerindedir. Ama parçanın içinde mi, uzantısında mı?
  • Bunu onSegment cevaplar: noktanın x’i ve y’si parçanın iki ucunun arasında mı? (Parçanın sınırlayıcı kutusu.)
  • T biçimi: C tam AB’nin üstünde → d₁ = 0, kutunun içinde → dokunuyorlar.
  • Uç uca: B ile C aynı nokta → dokunuyorlar.
  • Aynı doğru, üst üste: dört test de 0; uçlardan biri öbürünün kutusundaysa örtüşüyorlar.
  • Aynı doğru, ayrık: dört test de 0 ama hiçbir uç öbürünün kutusunda değil → kesişmiyorlar.
Dokunma “kesişme” sayılır mı?

Oyununuza bağlı bir tasarım kararı:

  • Mermi duvarın tam ucundan sıyırdı: vurdu mu? (Çoğu oyun: evet.)
  • İki duvar köşede birleşiyor: “kesişiyor” denirse seviye editörü her köşede hata verir. (Editörde: hayır.)
  • Görüş hattı bir köşeye teğet: görüyor mu?

Kodda özel durumları ayrı satırlar olarak tutmak bu yüzden iyi: istediğinizi açıp kapatırsınız.

Canlı: kesişim testi ✂️

  1. Ekranı okuyun: mavi AB merminin bir karedeki yolu, sarı CD duvar. Sağdaki tabloda dört yön testinin işareti (+, −, 0) ve altında iki koşul.
  2. B’yi duvarın öbür tarafına geçirin: ikinci koşul da ✔ olur ve kırmızı halka belirir.
  3. AB’yi, duvarın hizasında ama ucundan ötede tutun: “C ve D farklı tarafta” ✔ ama “A ve B farklı tarafta” ✘. Doğrular kesişir, parçalar kesişmez.
  4. “⇄ paralel yap”: işaretler çift çift aynı; kesişim yok.
  5. Bir ucu öbür parçanın tam üstüne getirmeye çalışın: işaret 0 olur. İşte özel durumların ve eşik payının gerektiği an.
🔎 DEMODA DENEYİN
  1. Ekranı okuyun: mavi AB merminin bir karedeki yolu, sarı CD duvar. Sağdaki tabloda dört yön testinin işareti (+, −, 0) ve altında iki koşul.
  2. B’yi duvarın öbür tarafına geçirin: ikinci koşul da ✔ olur ve kırmızı halka belirir.
  3. AB’yi, duvarın hizasında ama ucundan ötede tutun: “C ve D farklı tarafta” ✔ ama “A ve B farklı tarafta” ✘. Doğrular kesişir, parçalar kesişmez.
  4. “⇄ paralel yap”: işaretler çift çift aynı; kesişim yok.
  5. Bir ucu öbür parçanın tam üstüne getirmeye çalışın: işaret 0 olur. İşte özel durumların ve eşik payının gerektiği an.

Kod: kesişiyor mu?

🎒 Kodu okuyalım

ifadene demekneden böyle
d1 * d2 < 0iki sayı zıt işaretliArtı × eksi = eksi. İki ayrı if yerine tek çarpma. Biri 0 ise çarpım 0: genel duruma girmez, özel durumlara kalır.
d1 === 0 && onSegment(…)c, ab doğrusu üzerinde ve parçanın içinde&& kısa devre yapar: d1 sıfır değilse onSegment hiç çağrılmaz.
min(a.x, b.x) <= p.xp, a ile b’nin x aralığındaa mı solda b mi bilmiyoruz; min/max sırayı önemsiz kılar.
Python: a <= b <= czincirleme karşılaştırmaYalnızca Python’da; JS ve C#’ta iki ayrı karşılaştırma yazılır.
C#: 0ffloat türünde sıfırUnity’de Vector2 bileşenleri float’tır.
tam sayı / ondalık=== 0 yalnızca tam sayıda güvenliOndalıklı koordinatta abs(d1) < 1e-9 gibi bir eşik kullanın.
Önermeİki doğru parçasının kesişim testi sabit zamanlıdır: en çok 4 çapraz çarpım ve birkaç karşılaştırma, bölme yok. N duvar için mermi başına N test (G2’de bunu azaltacağız).

Çizimle: parçanın üstünde yürümek (t)

t = 0t = 0,25t = 0,5t = 0,75t = 1u = 0u = 1ABCDkesişim: t = 0,5 ve u = 0,5 → (3, 2)

Parçayı bir yolculuk gibi düşünün: t = 0 A’da, t = 1 B’de, t = 0,5 tam ortada. “Nerede kesişiyor?” sorusunun cevabı bir nokta değil, bir t sayısıdır; 0 ile 1 arasındaysa kesişim parçanın içindedir.

Nerede kesişiyor? Parametrik gösterim

Parçayı bir yolculuk olarak yazın: P(t) = A + t·r, r = B − A. t = 0’da A’dasınız, t = 1’de B’de, t = 0,5’te tam ortada.

Duvar da aynı şekilde: Q(u) = C + u·s, s = D − C.

Kesişimde P(t) = Q(u). İki tarafı s ile çapraz çarparsak (s × s = 0 olduğu için u yok olur):

$$t = \frac{(C - A) \times s}{r \times s} \qquad u = \frac{(C - A) \times r}{r \times s}$$

  • r × s = 0 → paralel (payda sıfır).
  • 0 ≤ t ≤ 1 ve 0 ≤ u ≤ 1 → kesişim iki parçanın da içinde.
  • Kesişim noktası: A + t·r.
Aynı örnek: A(0,0) B(6,4) · C(1,4) D(5,0)

r = (6, 4), s = (4, −4), C − A = (1, 4)

r × s = 6·(−4) − 4·4 = −40

(C − A) × s = 1·(−4) − 4·4 = −20

t = −20 / −40 = 0,5

Nokta = A + 0,5·r = (3, 2)

Sağlama: (C − A) × r = 1·4 − 4·6 = −20 → u = 0,5 → C + 0,5·s = (3, 2) ✔

🎮 t neden altın değerinde?t = “yolun yüzde kaçında çarptı”. Birden çok engel varsa en küçük t ilk çarpılandır. Hasar uzaklıkla azalıyorsa: uzaklık = t × menzil. Mermi izinin biteceği yer: A + t·r.

Kod: nerede çarptı ve ilk neye çarptı?

ilkCarpan bütün duvarları dener ve en küçük t’yi tutar: “en yakın engel”. Oyun motorlarındaki Raycast fonksiyonlarının yaptığı iş budur (yalnızca hangi duvarlara bakacağını bir uzaysal yapıyla azaltır, G2).

Oyunlarda: DOOM’da mermi nasıl isabet eder?

  • DOOM’da tabanca, pompalı ve makineli “mermi” göndermez: tetik çekilince namludan menzil boyunca tek bir doğru parçası çizilir (hitscan).
  • P_InterceptVector = bizim t formülümüz: payda iki yönün çapraz çarpımı, sonuç frac.
  • Yol üzerindeki bütün duvar çizgileri ve canavarlar (her canavar, kutusunun köşegeni olan bir çizgiyle temsil edilir) için frac hesaplanıp bir listeye konur.
  • P_TraverseIntercepts listeden her seferinde en küçük frac’lıyı seçer: en yakın engel önce. Engel mermiyi durdurursa (func yanlış döndürür) arkadakilere hiç bakılmaz.
  • Aynı kod “kapıyı kullan” tuşu, canavarların görüş kontrolü ve patlamaların duvar arkasına geçmemesi için de çalışır.

Canlı: DOOM usulü isabet 🔫

  1. Ekranı okuyun: yeşil nokta oyuncu; sarı çizgi merminin gittiği yer; kesikli gri çizgi menzilin geri kalanı. Koyu çubuklar duvar, mor daireler canavar.
  2. Yol üzerindeki her kesişim numaralı bir nokta: 1 en yakın. Sağ üstteki liste aynı kesişimleri t değerleriyle, küçükten büyüğe gösterir.
  3. İlk engelde “← DURDU” yazar; arkasındakiler “(ulaşmadı)”. Canavarın önüne duvar gelecek biçimde nişan alın: canavar vurulmaz.
  4. Aynı canavarı duvarın yanından görecek biçimde nişanı kaydırın: şimdi listede 1 numara o.
  5. “delici mermi”yi açın: canavarlardan geçer, ilk duvarda durur. Listede kaç “✔ vuruldu” var? (Tek fark: func canavarda “devam et” diyor.)
🔎 DEMODA DENEYİN
  1. Ekranı okuyun: yeşil nokta oyuncu; sarı çizgi merminin gittiği yer; kesikli gri çizgi menzilin geri kalanı. Koyu çubuklar duvar, mor daireler canavar.
  2. Yol üzerindeki her kesişim numaralı bir nokta: 1 en yakın. Sağ üstteki liste aynı kesişimleri t değerleriyle, küçükten büyüğe gösterir.
  3. İlk engelde “← DURDU” yazar; arkasındakiler “(ulaşmadı)”. Canavarın önüne duvar gelecek biçimde nişan alın: canavar vurulmaz.
  4. Aynı canavarı duvarın yanından görecek biçimde nişanı kaydırın: şimdi listede 1 numara o.
  5. “delici mermi”yi açın: canavarlardan geçer, ilk duvarda durur. Listede kaç “✔ vuruldu” var? (Tek fark: func canavarda “devam et” diyor.)

Hikâye: duvardan geçen mermi (tünelleme)

  • Mermiyi her karede bir nokta olarak düşünürseniz: bu karede duvarın önünde, sonraki karede arkasında. Hiçbir karede duvarın içinde değil → çarpışma yok!
  • Hızlı mermi + ince duvar + düşük kare hızı = tünelleme. Oyun tarihinin en eski hatalarından.
  • Çare tam olarak bu bölümdeki fonksiyon: merminin bu karedeki yolunu (önceki konum → yeni konum) duvarla kesiştirmek.
  • Motorlardaki adı: “sürekli çarpışma tespiti” (Unity: Collision Detection → Continuous). G3’te karakterler için de göreceğiz.
🥊 Super Smash Bros. Melee: yumruğun izi

Dövüş oyununda hızlı bir yumruk iki kare arasında rakibin içinden geçip ıskalayabilir. Melee bunu şöyle çözer: saldırının çarpışma şekli bir küredir; oyun, kürenin geçen karedeki yeri ile bu karedeki yerini birleştirir ve aradaki bütün hacmi (bir kapsül) vuruş alanı sayar.

Aynı fikir: konumu değil, kat edilen yolu sına.

Hız koşularıBirçok oyunun hız koşusu rekoru, karakteri tek karede çok hızlandırıp duvarın öbür tarafına geçirmeye dayanır. Hata aynı hata.
Quiz 2 · kesişim
Bir mermi bu karede A’dan B’ye gidiyor ve yolu üç duvarı kesiyor. Kesişim parametreleri t = 0,7, t = 0,2 ve t = 0,45. Mermi hangi duvarda durmalı?
t, A’dan B’ye yolun yüzde kaçında çarpıldığını söyler. En küçük t en önce karşılaşılan duvardır; mermi orada durur ve diğerlerine hiç ulaşmaz. DOOM’un P_TraverseIntercepts döngüsü tam olarak bunu yapar.

🐍 Laboratuvar: iki parça kesişiyor mu?

Dört yön testi. Genel durum: d1 ile d2 zıt işaretli ve d3 ile d4 zıt işaretli. Sonra dört özel durum: bir çapraz 0 ise o uç, öbür parçanın kutusunun içindeyse dokunuyorlar. Testlerde T biçimi, uç uca değen ve aynı doğru üzerinde üst üste binen parçalar da var.
LEVEL 2
Nokta çokgenin içinde mi?
ışın gönder, kesişim say

Problem: bu nokta şeklin içinde mi?

🎮 Oyunlarda
  • Harita: tıklanan nokta hangi bölgede / ülkede / odada?
  • Tetik alanı: oyuncu “boss odası” bölgesine girdi mi?
  • Arayüz: fare bu altıgen karonun, bu eğik düğmenin üstünde mi?
  • Çizim oyunları: oyuncunun çizdiği kapalı şeklin içinde hangi düşmanlar kaldı?
  • YZ: bu nokta yürünebilir alanın içinde mi? (Y9)
İlk akla gelenler ve neden yetmezler
  • Sınırlayıcı kutu: şekli saran dikdörtgenin içinde mi? Hızlı ama kaba: L ya da U biçimli bölgenin “boş” kısmı da kutunun içinde.
  • Merkeze uzaklık: yalnızca daireye benzeyen şekillerde çalışır.
  • Üçgenlere böl, tek tek dene: doğru ama önce üçgenlemek gerek (ayrı ve zor bir algoritma).

Sınırlayıcı kutu yine de değerli: ucuz bir ön eleme olarak. Kutunun dışındaysa kesin dışarıda; içindeyse asıl teste geç.

Fikir: ışın at, sınırı kaç kez geçtiğini say

  1. Noktadan herhangi bir yöne (biz sağa) sonsuz bir ışın gönder.
  2. Çokgenin kenarlarını kaç kez kesiyor, say.
  3. Tek sayı → içeride. Çift (ya da 0) → dışarıda.
Neden çalışır?

Işının çok uzağındaki ucu kesinlikle dışarıda. Oradan noktaya doğru geri yürüyün: sınırı her geçişinizde durumunuz değişir: dış → iç → dış → iç…

Noktaya vardığınızda tek sayıda geçiş yaptıysanız “iç”tesiniz.

Kapalı bir eğrinin düzlemi “iç” ve “dış” diye ikiye ayırdığı sezgisel olarak açıktır; matematikte adı Jordan eğri teoremidir.

Bir kenar ışını ne zaman keser? İki koşul:

  • (1) Noktanın y’si, kenarın iki ucunun y’leri arasında. (Yoksa ışın kenarın altından ya da üstünden geçer.)
  • (2) Kenarın o yükseklikteki x’i, noktanın sağında. (Işın sağa gidiyor; soldaki kesişim sayılmaz.)

Kenar A–B’nin py yüksekliğindeki x’i (benzer üçgenler):

$$x = A_x + (p_y - A_y)\,\frac{B_x - A_x}{B_y - A_y}$$

İçbükey, girintili, yüzlerce köşeli şekillerde de çalışır. Yatay kenarlar (Ay = By) 1. koşulu hiç sağlamaz: sıfıra bölme olmaz.

Çizimle: ışın at, kesişimleri say

(2, 2)1 kesişim → İÇERİDE(4, 4)2 kesişim → DIŞARIDA(2, 3)3 sayım → İÇERİDE (köşe iki kez sayılır)V çentiğikesikli: sınırlayıcı kutu

Her noktadan sağa bir ışın. Sınırı tek sayıda kesiyorsa nokta içeride, çift sayıda kesiyorsa dışarıda. (4, 4) sınırlayıcı kutunun içinde ama V çentiğinin boşluğunda: kutu testi yanılırdı, ışın testi yanılmıyor.

Elle izleme: beş köşeli bir şekil, üç nokta

Çokgen (üstünde V biçiminde bir çentik var): (1,1) → (6,1) → (6,5) → (3,3) → (1,5)

kenarp = (2, 2)p = (4, 4)p = (2, 3)
(1,1)–(6,1)y arada değily arada değily arada değil
(6,1)–(6,5)x = 6 > 2 ✔x = 6 > 4 ✔x = 6 > 2 ✔
(6,5)–(3,3)y arada değilx = 4,5 > 4 ✔x = 3 > 2 ✔
(3,3)–(1,5)y arada değilx = 2; sağda değilx = 3 > 2 ✔
(1,5)–(1,1)x = 1; sağda değilx = 1; sağda değilx = 1; sağda değil
sayım1 → İÇERİDE2 → DIŞARIDA3 → İÇERİDE

(4, 4) şeklin sınırlayıcı kutusunun içinde ama çentiğin boşluğunda: kutu testi burada yanılırdı.

⚠️ Köşe tuzağı: p = (2, 3)

Işın tam (3, 3) köşesinden geçiyor. O köşe iki kenarın ortak ucu. Bir kez mi sayılmalı, iki kez mi, hiç mi?

“y arada” koşulunu şöyle yazarsak sorun kendiliğinden çözülür:

(A.y > p.y) !== (B.y > p.y)

Yani: uçlardan tam biri ışının üstünde. Işınla aynı yükseklikteki uç “üstte değil” sayılır.

  • Çentiğin dibi (iki komşu da yukarıda): iki kenar da sayılır → +2 → sonuç değişmez ✔
  • Sivri tepe (iki komşu da aşağıda): hiçbiri sayılmaz → +0 ✔
  • Işının içinden geçtiği köşe (biri yukarıda, biri aşağıda): tam biri sayılır → +1 ✔

Canlı: içeride mi, dışarıda mı? 🎯

  1. Ekranı okuyun: gri nokta sizsiniz (fareyi izler). Mor kesikli çizgi sağa giden ışın; kırmızı numaralı noktalar kenarlarla kesişimleri. Altta sayım ve sonuç; içerideyken şekil yeşile döner.
  2. Fareyi soldan sağa yavaşça geçirin: sayı 4 → 3 → 2 → 1 → 0 diye düşer; tek sayılarda yeşil.
  3. Girintinin (şeklin sol ortasındaki çentik) içine girin: şeklin “kutusunun” içindesiniz ama dışarıdasınız.
  4. Tıklayıp noktayı sabitleyin, sonra bir köşeyi sürükleyip ışının üstünden geçirin: kesişim sayısı 2 artar ya da azalır, sonuç değişmez (köşe kuralı!).
  5. Noktayı bir köşeyle tam aynı yüksekliğe getirmeye çalışın. Sayım bozuluyor mu? (Bozulmamalı.)
🔎 DEMODA DENEYİN
  1. Ekranı okuyun: gri nokta sizsiniz (fareyi izler). Mor kesikli çizgi sağa giden ışın; kırmızı numaralı noktalar kenarlarla kesişimleri. Altta sayım ve sonuç; içerideyken şekil yeşile döner.
  2. Fareyi soldan sağa yavaşça geçirin: sayı 4 → 3 → 2 → 1 → 0 diye düşer; tek sayılarda yeşil.
  3. Girintinin (şeklin sol ortasındaki çentik) içine girin: şeklin “kutusunun” içindesiniz ama dışarıdasınız.
  4. Tıklayıp noktayı sabitleyin, sonra bir köşeyi sürükleyip ışının üstünden geçirin: kesişim sayısı 2 artar ya da azalır, sonuç değişmez (köşe kuralı!).
  5. Noktayı bir köşeyle tam aynı yüksekliğe getirmeye çalışın. Sayım bozuluyor mu? (Bozulmamalı.)

Kod: nokta çokgenin içinde mi?

🎒 Kodu okuyalım

ifadene demekneden böyle
for (let i = 0, j = n − 1; i < n; j = i++)i her köşeyi gezer; j hep “bir önceki” köşeİlk turda j = son köşe: böylece son → ilk kenarı da gezilir. j = i++: önce j’ye i’nin eski değeri atanır, sonra i artar.
Python: poly[i − 1]bir önceki köşei = 0 iken poly[−1] son elemandır: aynı hile, daha kısa.
(a.y > p.y) !== (b.y > p.y)iki karşılaştırmadan tam biri doğru“Uçlardan biri ışının üstünde, öbürü değil.” Köşe tuzağını da çözen satır.
ic = !icdoğruysa yanlış, yanlışsa doğru yapKesişimleri saymaya gerek yok; tek mi çift mi olduğu yeter.
/ (b.y − a.y)bölmeSıfıra bölünmez: 1. koşul sağlanmışsa a.y ≠ b.y.
ÖnermeIşın atma, N kenarlı çokgen için N adımda biter (her kenara bir kez bakılır); dışbükey ya da içbükey fark etmez. Pratik hızlandırma: önce sınırlayıcı kutu testi (4 karşılaştırma); çoğu nokta orada elenir.

🧪 Canlı laboratuvar: nokta çokgenin içinde mi?

Başlangıç kodu hep “dışarıda” der: sağdaki bütün noktalar gri, şeklin içindekiler kırmızı halkalı (yanlış). İki koşulu yazınca içerideki noktalar yeşile döner ve halkalar kaybolur. İki farklı içbükey şekille (biri “C” biçiminde) sınanıyor.

Oyunlarda: en iyi geometri, hiç geometri yapmamak

  • Paradox’un büyük strateji oyunlarında (Europa Universalis IV, Hearts of Iron IV) harita binlerce eyalete bölünür; her birinin sınırı yüzlerce köşeli. Fare hangi eyaletin üstünde?
  • Oyun bunu ışın atmayla yapmaz. Oyun dosyalarında provinces.bmp adlı bir resim vardır: her eyalet kendine özgü tek bir renkle boyanmıştır.
  • Yanındaki definition.csv dosyası rengi eyalete bağlar: “R 128, G 34, B 64 → 1 numaralı eyalet, Stockholm”.
  • Tıkladığınız noktanın resimdeki pikseli okunur, rengi tabloda aranır: eyalet bulundu. Maliyet sabit; sınırın karmaşıklığından bağımsız.
  • Mod yapanlar haritayı bir resim programında boyayarak düzenler; iki eyaleti yanlışlıkla aynı renge boyamak klasik bir mod hatasıdır.
Ders: önceden hesapla

Bir soruyu önceden hesaplayıp tabloya koyabiliyorsanız çalışma anında algoritma gerekmez (Y18).

Bedeli: bellek ve esneklik. Sınırlar oyun sırasında değişiyorsa (oyuncunun çizdiği bölgeler, hareketli tetik alanları, yakınlaştırınca keskin kalması gereken sınırlar) ışın atma gerekir.

🎮 Aynı hile 3B’de: renkle seçimSeviye editörleri ve bazı strateji oyunları “fare hangi nesnenin üstünde?” sorusunu şöyle çözer: sahneyi gizli bir tampona, her nesne ayrı düz bir renkle çizilmiş olarak işle; farenin altındaki pikselin rengini oku. Işın–üçgen testi yok.

Alan ve dönüş yönü: ayakkabı bağı formülü

Köşeleri sırayla dolaş, her kenar için bir çapraz çarpım topla:

$$\text{Alan} = \tfrac{1}{2}\,\Big|\sum_i (x_i\,y_{i+1} - x_{i+1}\,y_i)\Big|$$

Elle: dikdörtgen (0,0) → (4,0) → (4,3) → (0,3)

kenarxi·yi+1 − xi+1·yi
(0,0)→(4,0)0·0 − 4·0 = 0
(4,0)→(4,3)4·3 − 4·0 = 12
(4,3)→(0,3)4·3 − 0·3 = 12
(0,3)→(0,0)0·0 − 0·3 = 0
toplam24 → alan 12 ✔

Köşeleri ters sırada verirseniz toplam −24 çıkar: işaret = dönüş yönü. İçbükey çokgende de doğru. Neden çalışır: her terim, orijin ile o kenarın oluşturduğu üçgenin (işaretli) alanının 2 katıdır; dışarıda kalan parçalar ters işaretle gelip birbirini götürür.

🎮 Arka yüz ayıklamaBir 3B modelin üçgenleri hep aynı yönde (ör. saat yönünün tersine) sıralanır. Ekrana izdüşürüldükten sonra bir üçgen ters yönde görünüyorsa size sırtını dönmüştür; grafik kartı onu hiç boyamaz. Karar: bu formülün üç köşeli hâlinin (tek bir çapraz çarpımın) işareti. Kapalı bir modelde üçgenlerin kabaca yarısı böyle elenir.

🌱 Isınma kodu: çokgenin alanı

İki küçük hata var: döngü son kenarı (son köşe → ilk köşe) atlıyor ve köşeler ters yönde verildiğinde alan eksi çıkıyor. İpucu: pts[(i + 1) % pts.length] ve Math.abs. Az önceki dikdörtgen ilk test.
Quiz 3 · içeride mi?
U harfi biçimindeki bir bölgenin tam ortasındaki boşlukta (U’nun iki kolu arasında) duran bir nokta için sağa giden ışın kaç kenar keser ve sonuç nedir?
Işın sağ kola bir yüzünden girer, öbür yüzünden çıkar: 2 kesişim, çift, dışarıda. “Şeklin sınırlayıcı kutusunun içinde mi?” testi burada yanlış “içeride” derdi; ışın atma içbükey şekillerde de doğru cevap verir.

🎬 Ara: nokta üçgenin içinde mi?

Gamedev Maths: point in triangle
Sebastian Lague · 3 dk 53 sn · YouTube’da aç

Bu bölümdeki “içinde mi?” sorusunun en sık görülen hâli: üç çapraz çarpımın işaretiyle nokta-üçgen testi, Sebastian Lague’ın temiz çizimleriyle. Kısa; derste izlenebilir.

🤔 İzlerkenVideodaki yöntem bizim yön testimizi kaç kez kullanıyor? Nokta tam bir kenarın üstündeyse sonuç ne olur? Bir çokgeni üçgenlere bölüp (G10) bu testle “içinde mi?” sorusunu cevaplayabilir miyiz?
LEVEL 3
Dışbükey zarf
nokta bulutunun lastik bandı

Problem: bu noktaları saran en küçük dışbükey şekil

  • Tahtaya çiviler çakın, etrafına bir lastik bant geçirin ve bırakın. Bandın değdiği çiviler: dışbükey zarfın köşeleri.
  • Tanım: bütün noktaları içine alan en küçük dışbükey çokgen.
🎮 Oyunlarda
  • Çarpışma şekli: 5.000 üçgenli bir kaya modelinin yerine 20–30 köşeli zarfı çarpıştırılır. Dışbükey şekillerin çarpışma testi çok hızlıdır (G3).
  • Seçim ve gösterim: seçili birliklerin etrafındaki sınır; bir takımın kapladığı alan.
  • Ölçüm: bir nokta kümesinin en uzak iki noktası, en küçük saran dikdörtgeni: önce zarf, sonra yalnızca zarfın köşelerine bak.
  • Navmesh: engellerin sadeleştirilmesi (Y9).
İlk akla gelen: her çifti dene

İki nokta zarfın bir kenarıysa, bütün öbür noktalar o kenarın aynı tarafındadır. O hâlde: her nokta çifti için, kalan N − 2 noktanın yön testine bak.

N² çift × N test = N³. 1.000 nokta → 1 milyar yön testi.

Dört algoritma, bir problem

algoritmafikirmaliyetnot
Kaba kuvvether nokta çifti için: öbür noktaların hepsi aynı tarafta mı?N³yalnızca fikri görmek için
Hediye paketleme (Jarvis, 1973)en soldaki noktadan başla; her adımda bütün noktalara bakıp “en dışarıdaki” sonrakini seçN · hh = zarfın köşe sayısı; zarf küçükse çok hızlı
Graham taraması (1972)en alttaki noktaya göre açıya göre sırala; yığınla tek turN log Nders kitaplarındaki klasik sürüm
Monoton zincir (Andrew, 1979)x’e göre sırala; yığınla iki turN log Naçı hesabı yok; bugün en çok yazılan
Quickhullquicksort gibi: en uzak noktayı bul, kümeyi ikiye böl, içeride kalanları atortalama N log N3B’ye kolay genellenir; fizik motorlarının kullandığı

Hepsinin içinde aynı tek işlem var: yön testi. Fark, noktaları hangi sırayla ele aldıklarında. Biz monoton zinciri yazacağız: en kısa ve sınır durumlarına en dayanıklı olanı.

Fikir: sırala, sonra içe kıvrılanı at

  1. Noktaları x’e göre sırala (eşitlikte y’ye göre).
  2. 1. tur (soldan sağa): bir yığın tut. Her yeni nokta için: yığının son iki noktası ve yeni nokta yanlış yöne dönüyorsa (yön testi ≤ 0) sonuncuyu at; dönüş doğru olana kadar tekrarla. Sonra yeni noktayı ekle.
  3. 2. tur (sağdan sola): aynısı; zarfın öbür yarısı çıkar.
  4. İki zinciri birleştir (uç noktalar iki zincirde de var; birer kez yaz).
Neden doğru?

Zarfın çevresini bir yönde dolaşırsanız hep aynı tarafa dönersiniz (dışbükeylik bu demek). Ters yöne dönüş yaratan orta nokta, komşularını birleştiren çizginin iç tarafında kalmıştır: zarfın köşesi olamaz. Atmak güvenlidir.

Tanıdık üç parça

Sıralama (Mergesort sunumu) + yığın (Yığınlar sunumu) + yön testi (Level 0). Yeni hiçbir fikir yok; üçünün birleşimi.

Graham taramasından farkı yalnızca sıralama ölçütü: açı yerine x. Açı hesabı gerekmediği için daha kısa ve sayısal olarak daha sağlam; bugün en çok bu sürüm yazılır.

“≤ 0” mı, “< 0” mı?≤ 0: aynı kenar üzerindeki ara noktalar (doğrusal olanlar) atılır; zarfta yalnızca gerçek köşeler kalır. < 0 yazarsanız onlar da zarfta kalır. Çarpışma şekli için gereksiz köşe istemeyiz.

Çizimle: 7 noktanın zarfı

P1 (0, 0)P2 (1, 3) ✗P3 (2, 1) ✗P4 (3, 4) ✗P5 (4, 0)P6 (5, 2) ✗P7 (6, 1)1. tur (soldan sağa)kalın yeşil: alt zincirP1 → P5 → P7kırmızı ✗: bu turdayığından atılanlarkesikli yeşil: iki turbitince çıkan zarfince kesikli: noktalarıngeliş sırası (x’e göre)

Noktalar soldan sağa sırayla geliyor. Zincir “içe kıvrıldığında” (yön testi ≤ 0) son nokta atılıyor. Sonraki slayttaki tablo bu çizimin adım adım hesabıdır.

Elle izleme: 7 nokta, 1. tur

Sıralı noktalar: P1(0,0) P2(1,3) P3(2,1) P4(3,4) P5(4,0) P6(5,2) P7(6,1) · (y yukarı; “doğru dönüş” = çapraz > 0)

gelenyön testi (son iki, yeni)işlemyığın
P1—ekleP1
P2—ekleP1 P2
P3cross(P1,P2,P3) = 1·1 − 3·2 = −5P2’yi at, P3’ü ekleP1 P3
P4cross(P1,P3,P4) = 2·4 − 1·3 = +5ekleP1 P3 P4
P5cross(P3,P4,P5) = −7 → at; cross(P1,P3,P5) = −4 → atP4 ve P3’ü at, P5’i ekleP1 P5
P6cross(P1,P5,P6) = 4·2 − 0·5 = +8ekleP1 P5 P6
P7cross(P5,P6,P7) = −3 → at; cross(P1,P5,P7) = +4P6’yı at, P7’yi ekleP1 P5 P7

1. turun sonucu (alt zincir): P1 → P5 → P7.

2. tur (sağdan sola)

Sıra: P7, P6, P5, P4, P3, P2, P1.

  • P6, P5 eklenir; P4 gelince P5 atılır.
  • cross(P7, P6, P4) = 0: P6, P7–P4 kenarının tam üstünde. “≤ 0” kuralıyla atılır.
  • P3 eklenir; P2 gelince atılır.
  • P1 eklenir.

Üst zincir: P7 → P4 → P2 → P1.

Zarf: P1, P5, P7, P4, P2 (5 köşe). İçeride kalanlar: P3 ve P6.

DikkatP5 gelince iki nokta birden atıldı: if değil while gerekir. P4 önce eklendi, sonra atıldı: bir nokta yığına girip çıkabilir.

Canlı: zarfı adım adım kur 🪢

  1. Ekranı okuyun: gri noktalar girdi; üstlerindeki sayı x’e göre sıra numarası. Sarı: şu an bakılan nokta. Mavi çizgi: yığındaki zincir. Kırmızı: atılacak nokta. Yeşil: biten 1. tur zinciri.
  2. “1 adım ▶” ile ilerleyin ve alttaki cümleyi okuyun: her adım ya “yığına koy” ya “yığından at”. “◀” ile geri gelebilirsiniz.
  3. Bir noktanın önce yığına girip birkaç adım sonra atıldığını bulun; art arda iki atışın olduğu bir an yakalayın (az önceki tablodaki P5 gibi).
  4. “⏭ sonuç”tan sonra şeklin içine tıklayıp nokta ekleyin: zarf değişmez. Dışına tıklayın: zarf o noktaya doğru genişler, bazı eski köşeler içeride kalır.
  5. Nokta sayısını 40 yapın: zarfın köşe sayısı pek artmıyor. Rastgele noktalarda zarf, nokta sayısından çok daha küçüktür.
🔎 DEMODA DENEYİN
  1. Ekranı okuyun: gri noktalar girdi; üstlerindeki sayı x’e göre sıra numarası. Sarı: şu an bakılan nokta. Mavi çizgi: yığındaki zincir. Kırmızı: atılacak nokta. Yeşil: biten 1. tur zinciri.
  2. “1 adım ▶” ile ilerleyin ve alttaki cümleyi okuyun: her adım ya “yığına koy” ya “yığından at”. “◀” ile geri gelebilirsiniz.
  3. Bir noktanın önce yığına girip birkaç adım sonra atıldığını bulun; art arda iki atışın olduğu bir an yakalayın (az önceki tablodaki P5 gibi).
  4. “⏭ sonuç”tan sonra şeklin içine tıklayıp nokta ekleyin: zarf değişmez. Dışına tıklayın: zarf o noktaya doğru genişler, bazı eski köşeler içeride kalır.
  5. Nokta sayısını 40 yapın: zarfın köşe sayısı pek artmıyor. Rastgele noktalarda zarf, nokta sayısından çok daha küçüktür.

Kod: dışbükey zarf

🎒 Kodu okuyalım

ifadene demekneden böyle
points.slice().sort(…)kopyasını al, kopyayı sıralasort diziyi yerinde değiştirir; çağıranın dizisini bozmamak için önce kopya.
(a, b) => a.x − b.x || a.y − b.yx’e göre; x’ler eşitse y’ye göre||: soldaki 0 (eşit) ise sağdakine bakılır. Karşılaştırma fonksiyonu negatif / 0 / pozitif döndürür.
h[h.length − 2], h[h.length − 1]yığının sondan ikinci ve son elemanıPython’da kısaca h[-2], h[-1].
while (… <= 0) h.pop()dönüş düzelene kadar atif olsaydı art arda iki kötü nokta kaçardı (izlemedeki P5).
h.pop() (döngüden sonra)zincirin son noktasını çıkarO nokta öbür zincirin ilk noktası; iki kez yazılmasın.
P.slice().reverse()ters sırada kopya2. tur aynı fonksiyon; yalnızca noktalar sağdan sola.
ÖnermeMonoton zincir N log N zamanda çalışır. Sıralama N log N; iki turda her nokta yığına bir kez girer, en çok bir kez çıkar: turlar toplam 2N ekleme ve en çok 2N çıkarma. Daha hızlısı da olamaz: zarf bulabilen bir algoritma sayıları sıralamak için de kullanılabilir.

🐍 Laboratuvar: dışbükey zarf

Monoton zincir: sırala; soldan sağa bir yığınla zincir kur (while len(h) >= 2 and cross(h[-2], h[-1], p) <= 0: h.pop()); sağdan sola tekrar et; iki zinciri son elemanları hariç birleştir. Testler sonucu sıralı beklediği için en sonda sorted(...) döndürün. Elle izlediğimiz 7 nokta da testlerde.

Oyunlarda: çarpışma şekli nereden gelir?

  • Bir 3B modeli fizik motoruna “dışbükey” diye verdiğinizde motor köşe noktalarının dışbükey zarfını hesaplar ve çarpışmada onu kullanır.
  • Unity: MeshCollider.convex = true. Zarf en çok 255 üçgen olabilir; model daha ayrıntılıysa motor sadeleştirir. Hareket eden (Rigidbody’li) nesnelerde dışbükey olmayan ağ çarpıştırıcıya izin verilmez.
  • PhysX (Unity ve Unreal’ın 3B fiziği) zarfı Quickhull ile “pişirir”: bu bölümdeki problemin 3B hâli.
  • İçbükey nesne (sandalye, kemer, halka)? Tek zarf boşlukları doldurur: sandalyenin bacaklarının arasından top geçemez. Çözüm: modeli birkaç dışbükey parçaya ayırmak. Unreal’ın “Auto Convex Collision” aracı ve yaygın V-HACD kütüphanesi bunu otomatik yapar.
🎮 Oyuncu ne zaman fark eder?
  • Kemerin altından yürüyemiyorsunuz; halkanın ortasından top geçmiyor.
  • Mermi, kayanın çukur yerinde “havada” duruyor.
  • Düşen kutu, L biçimli masanın boş köşesine oturuyor.

Hepsi aynı sebep: gördüğünüz model ile çarpışan şekil (zarf) aynı değil.

Neden bu zahmet?İki dışbükey şeklin çarpışma testi, köşe sayısıyla doğrusal büyür ve hep doğru çalışır (G3: ayıran eksen teoremi, GJK). İçbükey şekiller için böyle hızlı ve genel bir test yok.
Quiz 4 · zarf
Monoton zincirde bir nokta yığına eklendikten sonra yığından atılıyor. Bu nokta hakkında ne söylenebilir?
Atılan nokta, komşularının oluşturduğu çizginin “iç” tarafında kalmıştır; o zincirde bir daha yeri yoktur. Her nokta tur başına bir kez girip en çok bir kez çıktığı için turlar doğrusal zaman alır; algoritmanın maliyetini baştaki sıralama belirler.
LEVEL 4
Hepsi bir arada
tam kod, araçlar, kapanış

📦 Tam kod: p5.js ile geometri oyun alanı

Bu 20 satır + sunum boyunca yazdığımız dört fonksiyon: cross, inside, polygonArea, convexHull.

🎮 Çalıştırın, kopyalayın, bozun

▶ Eksiksiz örneği aç — yaklaşık 70 satır: tıklayarak nokta ekleyin; zarf, alanı, ışının kestiği kenarlar ve “fare içeride mi?” canlı hesaplanır.

Deneyin:

  • convexHull’da <= 0’ı >= 0 yapın: ne çıkıyor?
  • Noktaları hareket ettirin (her karede küçük rastgele adım): zarf canlı güncellensin.
  • İki ayrı renk nokta kümesi tutun; zarfları kesişiyor mu? (ipucu: segmentsIntersect)
  • Fareden rastgele yöne “mermi” atın: ilkCarpan ile zarfın hangi kenarına çarptığını bulun.

🧰 Motorlarda ve kütüphanelerde

p5.jsp5.collide2D eklentisicollideLineLine, collidePointPoly, collidePolyPoly… hazır 2B testler
UnityPhysics2D.Linecast / Raycast · Collider2D.OverlapPoint · PolygonCollider2D · MeshCollider.convexParça–şekil kesişimi, nokta şeklin içinde mi, otomatik dışbükey zarf
UnrealFMath::SegmentIntersection2D · FGeomTools2D · Auto Convex CollisionAynı fonksiyonların motor sürümleri; içbükey modeli dışbükey parçalara ayırma
GodotGeometry2Dsegment_intersects_segment, is_point_in_polygon, convex_hull, decompose_polygon_in_convex
PythonShapelySeviye araçları ve analiz için: kesişim, birleşim, zarf, alan
C++ / C#Clipper2Çokgen kesme, birleştirme, genişletme (navmesh üreticilerinin iş atı)

Hazır fonksiyonu kullanın; ama işaret hatası, eşik payı ve “köşeden geçen ışın” sorunlarını ancak içini bilen biri teşhis edebilir.

🤖 Bu konuyu bir YZ kod asistanıyla yazarken

Sık yapılan hatalar — kontrol edin
  • Ekran koordinatında (y aşağı) sol/sağ işaretini ters almak
  • Kesişimde uç dokunmalarını ve paralel durumu (payda 0) atlamak
  • Işın atmada köşeden geçen ışını iki kez saymak
  • Zarfta while yerine if; doğrusal ve yinelenen noktalar
  • Kayan nokta karşılaştırmasında == 0 kullanmak
İyi bir istek (prompt) örneği

“p5.js. {x, y} noktalarla çalışan bir geo.js yaz: cross, segmentsIntersect (uç dokunması ve üst üste binme dahil), hitPoint (t parametresiyle), inside (ışın atma, köşe kuralı açıklamalı), polygonArea, convexHull (monoton zincir, doğrusal noktalar hariç). Koordinat sistemi: y aşağı. Her fonksiyon için console.assert ile en az 5 test: paralel, doğrusal, uç uca, içbükey şekil, yinelenen nokta.”

  • Koordinat sistemini (y yukarı mı aşağı mı) mutlaka söyleyin
  • Sınır durumlarını siz sayın; testleri onlar için isteyin
  • Görsel bir test sayfası isteyin: fareyle sürükleyip gözle doğrulayın

💬 Tartışma soruları

Yanınızdakiyle 2 dakika konuşun, sonra sınıfça tartışalım
  1. Bir strateji oyununda bölge seçimi için “renkli resim” (Paradox) ile “ışın atma” arasında nasıl seçim yapardınız? Hangi oyun özelliği kararı değiştirir?
  2. DOOM’da her canavar, mermi testinde tek bir çizgiyle temsil ediliyor ve (özgün oyunda) sonsuz yükseklikte sayılıyor. Oyuncular bunu yıllarca fark etmedi. Bir sadeleştirme ne zaman “hata”, ne zaman “tasarım” olur?
  3. Mermi duvarın tam köşesinden geçti: bir oyuncu “vurdum”, öbürü “ıskaladın” diyor. Eşik payını kimin lehine ayarlarsınız? Rekabetçi bir oyunda cevap değişir mi?
  4. Karakterin çarpışma şekli olarak modelin kendisi yerine dışbükey zarfı kullanılıyor. Hangi nesnelerde bu kabul edilemez olur ve ne yaparsınız?
  5. Bu sunumdaki dört algoritmanın hepsi tek bir formüle dayanıyor. Dönem boyunca böyle “küçük ama her yerde” başka hangi fikirleri gördünüz?

🚀 Proje ve araştırma önerileri

  1. [kolay] Lazer ve aynalar — p5.js’te bir lazer ışını ve fareyle döndürülen ayna parçaları: ilkCarpan ve yansıma; hedefi vurunca seviye biter.
  2. [kolay] Bölge seçici — Elle çizilmiş 6 içbükey bölgeli bir harita; fare hangi bölgenin üstündeyse o parlasın (ışın atma). Aynısını renkli resim hilesiyle yapıp karşılaştırın.
  3. [orta] DOOM usulü nişancı — Yukarıdan bakışlı 2B harita (duvar çizgileri) + canavarlar; hitscan silah: bütün kesişimleri toplayıp en küçük t’den işleyin. Delici silah ekleyin.
  4. [orta] Tünelleme deneyi — Hızlı bir mermi ve ince bir duvar: nokta testiyle kaç karede bir kaçırıyor? Parça kesişimiyle düzeltin; kare hızına göre grafik.
  5. [zor] Çizerek yakala — Oyuncu fareyle kapalı bir şekil çizsin; içindeki düşmanlar (ışın atma) yakalansın. Kendini kesen çizimleri kesişim testiyle reddedin.
  6. [araştırma] Sağlamlık avı — Yön testini ondalıklı sayılarla milyonlarca “neredeyse doğrusal” üçlüde çalıştırın; A,B,C ile B,C,A sıralarının farklı cevap verdiği durumları bulun ve görselleştirin.

Neler öğrendik? 🎒

  • Yön testi: tek çapraz çarpım; işaret = taraf, büyüklük = alan × 2. Ekranda y aşağı! (DOOM: P_PointOnLineSide; Mario 64: üç test = nokta üçgende.)
  • Kesişim: dört yön testi; t = “yolun yüzde kaçında”; ilk engel = en küçük t. (DOOM’un mermisi; tünellemenin ilacı.)
  • Nokta çokgende: ışın gönder, kesişim say: tek = içeride; köşe kuralı tek satırda. Önceden hesaplanabiliyorsa renkli resim (Paradox).
  • Alan: ayakkabı bağı; işareti dönüş yönü (arka yüz ayıklama).
  • Dışbükey zarf: sırala + yığın + yön testi = N log N; fizik motorlarının çarpışma şekli.
  • Hepsi çarpma ve çıkarma; trigonometri yok. En büyük tuzak: sınır durumları ve eşik payı.
  • Sonraki: uzaysal bölümleme: “yakınımda kim var?” sorusunu binlerce nesne için hızlı cevaplamak.

🎮 Bu sunumdaki oyunlar

Adı geçen oyunu tanımıyorsanız: oyuncuların kullandığı wiki sayfası ve oyunu kısaca gösteren bir video. ▶ video slaydın üstünde açılır (Esc ile kapanır); 🎯 işaretli bağlantılar doğrudan bu sunumdaki konuya gider.

Doom 1993 · nişancı
Türü popülerleştiren nişancı; kaynak kodu 1997’de açıldı.

Kaynaklar: videolar 🎬

Kaynaklar 📚

NEXT LEVEL ▶
Uzaysal Bölümleme
Izgara, uzaysal hash, dörtlü ağaç, BVH
SONRAKİ SUNUM ▶