Salah satu cara cepat dan kotor menggunakan pembagian bola rekursif . Dimulai dengan triangulasi permukaan bumi, secara rekursi membelah setiap segitiga dari sebuah sudut melintang ke tengah sisi terpanjangnya. (Idealnya Anda akan membagi segitiga menjadi dua bagian dengan diameter yang sama atau bagian dengan luas yang sama, tetapi karena itu melibatkan beberapa perhitungan fiddly, saya hanya membagi sisi menjadi dua: ini menyebabkan berbagai segitiga pada akhirnya sedikit berbeda ukurannya, tetapi yang sepertinya tidak penting untuk aplikasi ini.)
Tentu saja Anda akan mempertahankan pembagian ini dalam struktur data yang memungkinkan dengan cepat mengidentifikasi segitiga di mana titik arbitrer berada. Pohon biner (berdasarkan pada panggilan rekursif) berfungsi dengan baik: setiap kali sebuah segitiga terbelah, pohon itu terbelah pada simpul segitiga itu. Data mengenai bidang yang terbelah dipertahankan, sehingga Anda dapat dengan cepat menentukan di sisi mana saja titik arbitrer berada: yang akan menentukan apakah akan melakukan perjalanan ke kiri atau ke kanan melewati pohon.
(Apakah saya mengatakan pemisahan "bidang"? Ya - jika memodelkan permukaan bumi sebagai bola dan menggunakan koordinat geosentris (x, y, z), maka sebagian besar perhitungan kami dilakukan dalam tiga dimensi, di mana sisi segitiga adalah potongan persimpangan bola dengan pesawat melalui asalnya. Ini membuat perhitungan cepat dan mudah.)
Saya akan menggambarkan dengan menunjukkan prosedur pada satu oktan bola; tujuh oktan lainnya diproses dengan cara yang sama. Oktan seperti itu adalah segitiga 90-90-90. Dalam grafik saya, saya akan menggambar segitiga Euclidean yang mencakup sudut yang sama: mereka tidak terlihat sangat bagus sampai mereka menjadi kecil, tetapi mereka dapat dengan mudah dan cepat digambar. Berikut adalah segitiga Euclidean yang sesuai dengan oktan: ini adalah awal dari prosedur.
Karena semua pihak memiliki panjang yang sama, satu dipilih secara acak sebagai "terpanjang" dan dibagi:
Ulangi ini untuk masing-masing segitiga baru:
Setelah n langkah kita akan memiliki 2 ^ n segitiga. Berikut adalah situasi setelah 10 langkah, menunjukkan 1024 segitiga dalam oktan (dan 8192 pada keseluruhan bola):
Sebagai ilustrasi lebih lanjut, saya menghasilkan titik acak dalam oktan ini dan melakukan perjalanan pohon pembagian sampai sisi terpanjang segitiga mencapai kurang dari 0,05 radian. Segitiga (cartesian) ditunjukkan dengan titik probe berwarna merah.
Secara kebetulan, untuk mempersempit lokasi titik ke satu derajat garis lintang (kurang-lebih), Anda akan mencatat bahwa ini sekitar 1/60 radian dan mencakup sekitar (1/60) ^ 2 / (Pi / 2) = 1/6000 dari permukaan total. Karena setiap subdivisi kira-kira membagi ukuran segitiga, sekitar 13 hingga 14 subdivisi dari oktan akan melakukan trik. Itu tidak banyak perhitungan - seperti yang akan kita lihat di bawah ini - membuatnya efisien untuk tidak menyimpan pohon sama sekali, tetapi untuk melakukan pembagian dengan cepat. Pada awalnya Anda akan mencatat di mana oktan titik terletak - yang ditentukan oleh tanda-tanda tiga koordinatnya, yang dapat dicatat sebagai angka biner tiga digit - dan pada setiap langkah Anda ingin mengingat apakah titik terletak di sebelah kiri (0) atau kanan (1) dari segitiga. Itu memberi angka biner 14 digit lagi. Anda dapat menggunakan kode ini untuk mengelompokkan poin arbitrer.
(Secara umum, ketika dua kode ditutup sebagai angka biner aktual, titik-titik yang sesuai dekat; namun, poin masih dapat ditutup dan memiliki kode yang sangat berbeda. Pertimbangkan dua titik terpisah satu meter terpisah dengan Khatulistiwa, misalnya: kode mereka harus berbeda bahkan sebelum titik biner, karena mereka berada di oktan yang berbeda. Hal semacam ini tidak dapat dihindari dengan pembagian ruang yang tetap.)
Saya menggunakan Mathematica 8 untuk mengimplementasikan ini: Anda dapat mengambil apa adanya atau sebagai pseudocode untuk implementasi di lingkungan pemrograman favorit Anda.
Tentukan pada sisi mana bidang 0-ab titik p terletak pada:
side[p_, {a_, b_}] := If[Det[{p, a, b}] >= 0, left, right];
Perbaiki segitiga abc berdasarkan titik p.
refine[p_, {a_, b_, c_}] := Block[{sides, x, y, z, m},
sides = Norm /@ {b - c, c - a, a - b} // N;
{x, y, z} = RotateLeft[{a, b, c}, First[Position[sides, Max[sides]]] - 1];
m = Normalize[Mean[{y, z}]];
If[side[p, {x, m}] === right, {y, m, x}, {x, m, z}]
]
Gambar terakhir diambil dengan menampilkan oktan dan, di atas itu, dengan menjadikan daftar berikut ini sebagai satu set poligon:
p = Normalize@RandomReal[NormalDistribution[0, 1], 3] (* Random point *)
{a, b, c} = IdentityMatrix[3] . DiagonalMatrix[Sign[p]] // N (* First octant *)
NestWhileList[refine[p, #] &, {a, b, c}, Norm[#[[1]] - #[[2]]] >= 0.05 &, 1, 16]
NestWhileList
berulang kali menerapkan operasi ( refine
) ketika suatu kondisi berkaitan (segitiga besar) atau sampai jumlah operasi maksimum telah tercapai (16).
Untuk menampilkan triangulasi penuh oktan, saya mulai dengan oktan pertama dan mengulangi penyempurnaan sepuluh kali. Ini dimulai dengan sedikit modifikasi refine
:
split[{a_, b_, c_}] := Module[{sides, x, y, z, m},
sides = Norm /@ {b - c, c - a, a - b} // N;
{x, y, z} = RotateLeft[{a, b, c}, First[Position[sides, Max[sides]]] - 1];
m = Normalize[Mean[{y, z}]];
{{y, m, x}, {x, m, z}}
]
Perbedaannya adalah bahwa split
mengembalikan kedua bagian dari segitiga inputnya daripada segitiga di mana titik tertentu berada. Triangulasi penuh diperoleh dengan iterasi ini:
triangles = NestList[Flatten[split /@ #, 1] &, {IdentityMatrix[3] // N}, 10];
Untuk memeriksanya, saya menghitung ukuran setiap segitiga dan melihat kisarannya. ("Ukuran" ini proporsional dengan sosok berbentuk piramida yang digantikan oleh setiap segitiga dan pusat bola; untuk segitiga kecil seperti ini, ukuran ini pada dasarnya sebanding dengan area bulatnya.)
Through[{Min, Max}[Map[Round[Det[#], 0.00001] &, triangles[[10]] // N, {1}]]]
{0,00523, 0,00739}
Dengan demikian ukuran bervariasi naik atau turun sekitar 25% dari rata-rata mereka: yang tampaknya masuk akal untuk mencapai cara yang hampir seragam untuk mengelompokkan poin.
Dalam pemindaian kode ini, Anda tidak akan melihat trigonometri : satu-satunya tempat yang diperlukan, jika sama sekali, adalah konversi bolak-balik antara koordinat bola dan koordinat kartesius. Kode juga tidak memproyeksikan permukaan bumi ke peta apa pun, sehingga menghindari distorsi yang menyertainya. Kalau tidak, itu hanya menggunakan rata-rata ( Mean
), Teorema Pythagoras ( Norm
) dan penentu 3 oleh 3 ( Det
) untuk melakukan semua pekerjaan. (Ada beberapa perintah manipulasi daftar sederhana seperti RotateLeft
dan Flatten
, juga, bersama dengan pencarian sisi terpanjang dari setiap segitiga.)