Diberikan array x, poin y, bagaimana cara saya mengurutkan poin array ini dalam urutan searah jarum jam (sekitar titik tengah rata-rata keseluruhan)? Tujuan saya adalah meneruskan poin ke fungsi pembuatan garis untuk menghasilkan sesuatu yang terlihat "solid", cembung mungkin tanpa garis yang berpotongan.
Untuk apa nilainya, saya menggunakan Lua, tetapi kodesemu apa pun akan dihargai.
Pembaruan: Untuk referensi, ini adalah kode Lua berdasarkan pada jawaban Ciamej yang sangat baik (abaikan awalan "aplikasi" saya):
function appSortPointsClockwise(points)
local centerPoint = appGetCenterPointOfPoints(points)
app.pointsCenterPoint = centerPoint
table.sort(points, appGetIsLess)
return points
end
function appGetIsLess(a, b)
local center = app.pointsCenterPoint
if a.x >= 0 and b.x < 0 then return true
elseif a.x == 0 and b.x == 0 then return a.y > b.y
end
local det = (a.x - center.x) * (b.y - center.y) - (b.x - center.x) * (a.y - center.y)
if det < 0 then return true
elseif det > 0 then return false
end
local d1 = (a.x - center.x) * (a.x - center.x) + (a.y - center.y) * (a.y - center.y)
local d2 = (b.x - center.x) * (b.x - center.x) + (b.y - center.y) * (b.y - center.y)
return d1 > d2
end
function appGetCenterPointOfPoints(points)
local pointsSum = {x = 0, y = 0}
for i = 1, #points do pointsSum.x = pointsSum.x + points[i].x; pointsSum.y = pointsSum.y + points[i].y end
return {x = pointsSum.x / #points, y = pointsSum.y / #points}
end
ipairs(tbl)
bawaan yang mengulangi indeks dan nilai tbl dari 1 hingga #tbl. Jadi untuk perhitungan penjumlahan, Anda dapat melakukan ini, yang menurut kebanyakan orang terlihat lebih bersih:for _, p in ipairs(points) do pointsSum.x = pointsSum.x + p.x; pointsSum.y = pointsSum.y + p.y end
ipairs
secara signifikan lebih lambat daripada numerik untuk loop.Jawaban:
Pertama, hitung titik pusatnya. Kemudian urutkan titik menggunakan algoritme pengurutan apa pun yang Anda suka, tetapi gunakan rutin perbandingan khusus untuk menentukan apakah satu titik kurang dari yang lain.
Anda dapat memeriksa apakah satu titik (a) adalah ke kiri atau ke kanan yang lain (b) dalam kaitannya dengan pusat dengan perhitungan sederhana ini:
jika hasilnya nol, maka mereka berada di garis yang sama dari pusat, jika itu positif atau negatif, maka itu di satu sisi atau yang lain, jadi satu titik akan mendahului yang lain. Dengan menggunakannya, Anda dapat membuat relasi yang kurang dari itu untuk membandingkan poin dan menentukan urutan kemunculannya dalam array yang diurutkan. Tetapi Anda harus menentukan di mana awal dari urutan itu, maksud saya sudut apa yang akan menjadi permulaan (misalnya setengah positif dari sumbu x).
Kode untuk fungsi perbandingan dapat terlihat seperti ini:
Ini akan memesan titik searah jarum jam mulai dari jam 12. Poin pada "jam" yang sama akan dipesan mulai dari yang jauh dari pusat.
Jika menggunakan tipe integer (yang tidak benar-benar ada dalam Lua) Anda harus memastikan bahwa variabel det, d1 dan d2 adalah tipe yang akan dapat menampung hasil perhitungan yang dilakukan.
Jika Anda ingin mencapai sesuatu yang terlihat solid, cembung mungkin, maka saya kira Anda sedang mencari Convex Hull . Anda dapat menghitungnya menggunakan Graham Scan . Dalam algoritma ini, Anda juga harus mengurutkan titik searah jarum jam (atau berlawanan arah jarum jam) mulai dari titik pivot khusus. Kemudian Anda mengulangi langkah loop sederhana setiap kali memeriksa apakah Anda belok kiri atau kanan menambahkan poin baru ke cembung cembung, pemeriksaan ini didasarkan pada produk silang seperti pada fungsi perbandingan di atas.
Edit:
Menambahkan satu lagi jika pernyataan
if (a.y - center.y >= 0 || b.y - center.y >=0)
untuk memastikan bahwa titik yang memiliki x = 0 dan negatif y diurutkan mulai dari yang lebih jauh dari pusat. Jika Anda tidak peduli dengan urutan poin pada 'jam' yang sama, Anda dapat menghilangkan pernyataan if ini dan selalu kembalia.y > b.y
.Mengoreksi pernyataan if pertama dengan menambahkan
-center.x
dan-center.y
.Menambahkan pernyataan if kedua
(a.x - center.x < 0 && b.x - center.x >= 0)
. Itu adalah pengawasan yang jelas bahwa itu hilang. Pernyataan if bisa ditata ulang sekarang karena beberapa cek berlebihan. Misalnya, jika kondisi pertama dalam pernyataan pertama jika salah, maka kondisi pertama dari kedua jika harus benar. Saya memutuskan, bagaimanapun, untuk meninggalkan kode karena demi kesederhanaan. Sangat mungkin bahwa kompiler akan mengoptimalkan kode dan tetap menghasilkan hasil yang sama.sumber
atan()
, tidak ada akar kuadrat, dan bahkan tidak ada divisi. Ini adalah contoh pemikiran grafis komputer yang bagus. Culling semua case mudah sesegera mungkin, dan bahkan dalam hard case, hitung sesedikit mungkin untuk mengetahui jawaban yang diperlukan.Pendekatan alternatif yang menarik untuk masalah Anda adalah menemukan perkiraan minimum untuk Traveling Salesman Problem (TSP), yaitu. rute terpendek yang menghubungkan semua titik Anda. Jika poin Anda membentuk bentuk cembung, itu harus menjadi solusi yang tepat, jika tidak, itu akan tetap terlihat bagus (bentuk "padat" dapat didefinisikan sebagai salah satu yang memiliki rasio perimeter / area rendah, yang kami optimalkan di sini) .
Anda dapat menggunakan implementasi pengoptimal apa pun untuk TSP, yang saya yakin Anda dapat menemukan satu ton dalam bahasa pilihan Anda.
sumber
Yang Anda minta adalah sistem yang dikenal sebagai koordinat kutub . Konversi dari Cartesian ke koordinat polar mudah dilakukan dalam bahasa apa pun. Rumus dapat ditemukan di bagian ini .
Setelah mengkonversi ke koordinat kutub, cukup urutkan berdasarkan sudut, theta.
sumber
Versi lain (mengembalikan true jika a datang sebelum b dalam arah berlawanan):
Ini lebih cepat, karena kompiler (diuji pada Visual C ++ 2015) tidak menghasilkan lompatan untuk menghitung dax, hari, dbx, dby. Di sini rakitan keluaran dari kompiler:
Nikmati.
sumber
Akhirnya Anda mendapatkan Anticlockwize verts diurutkan
daftar. Balikkan () .................. Searah jarum jam
sumber