Hitung titik acak (piksel) dalam lingkaran (gambar)

19

Saya memiliki gambar yang berisi lingkaran di lokasi tertentu, dan dengan diameter tertentu. Yang perlu saya lakukan adalah untuk dapat menghitung titik acak di dalam lingkaran, dan kemudian memanipulasi piksel kata titik berkorelasi. Saya sudah memiliki kode berikut:

private Point CalculatePoint()
{
    var angle = _random.NextDouble() * ( Math.PI * 2 );
    var x = _originX + ( _radius * Math.Cos( angle ) );
    var y = _originY + ( _radius * Math.Sin( angle ) );
    return new Point( ( int )x, ( int )y );
}

Dan itu berfungsi dengan baik untuk menemukan semua titik di lingkaran lingkaran, tetapi saya membutuhkan semua titik dari mana saja di lingkaran. Jika ini tidak masuk akal, beri tahu saya dan saya akan melakukan yang terbaik untuk mengklarifikasi.

DMill
sumber
Periksa pembaruan.
David Gouveia
3
Pertanyaan bagus dalam arti bahwa secara tidak sengaja membuat distribusi tertimbang adalah kesalahan umum.
Tim Holt

Jawaban:

35

Jika Anda menginginkan solusi sederhana, acak saja radiusnya:

private Point CalculatePoint()
{
    var angle = _random.NextDouble() * Math.PI * 2;
    var radius = _random.NextDouble() * _radius;
    var x = _originX + radius * Math.Cos(angle);
    var y = _originY + radius * Math.Sin(angle);
    return new Point((int)x,(int)y);
}

Namun itu menghasilkan poin Anda lebih terkonsentrasi ke pusat lingkaran:

masukkan deskripsi gambar di sini

Untuk mendapatkan distribusi yang seragam, lakukan perubahan berikut pada algoritme:

var radius = Math.Sqrt(_random.NextDouble()) * _radius;

Yang akan memberikan hasil sebagai berikut:

masukkan deskripsi gambar di sini

Untuk informasi lebih lanjut, periksa tautan berikut: MathWorld - Disk Point Picking .

Dan akhirnya inilah demonstrasi JsFiddle sederhana yang membandingkan kedua versi algoritma.

David Gouveia
sumber
1
Jawaban yang sangat bagus. Hanya satu hal untuk ditambahkan: jangan lupa untuk
menambak
Oop Anda ketuk bertemu dengannya - tidak melihat posting ini ketika saya memposting milikku. Situs wolfram adalah sumber yang luar biasa untuk hal semacam ini.
Tim Holt
1
@TimHolt Terjadi sepanjang waktu :)
David Gouveia
Apakah ini dengan asumsi pusat lingkaran berada pada 0,0?
jjxtra
@PsychoDad Pusat lingkarannya adalah (_originX, _originY)
David Gouveia
5

JANGAN gunakan r dan theta secara acak! Ini menciptakan distribusi tertimbang dengan lebih banyak poin di tengah. Halaman ini menggambarkan dengan baik ...

http://mathworld.wolfram.com/DiskPointPicking.html

Inilah metode yang membuat distribusi non-tertimbang ...

var r = rand(0,1)
var theta = rand(0,360)

var x = sqrt(r) * cos(theta)
var y = sqrt(r) * sin(theta)
Tim Holt
sumber
Ups duplikat dari jawaban yang dipilih: P
Tim Holt
Saya bingung karena Anda mengatakan untuk tidak menggunakan r acak dan theta karena membuat distribusi tertimbang maka kode yang Anda tunjukkan bahwa Anda mengklaim membuat distribusi non-tertimbang menghasilkan r dalam kisaran [0,1]. Apakah Anda berniat untuk melakukan root pada nomor acak?
PeteUK
Ya, melakukan akar kuadrat dari jari-jari (asalkan 0-1) mengurangi konsentrasi titik yang tak terduga di tengah. Lihat tautan Wolfram yang saya poskan, yang menggambarkannya dan menjelaskannya dengan matematika lebih baik daripada yang saya bisa.
Tim Holt
Salahku. Saya melihat Anda melakukan sqrt (r) saat menghitung x dan y.
PeteUK
4

Anda setengah jalan di sana. Selain menghasilkan sudut acak, hanya menghasilkan jarak acak, kurang dari atau sama dengan jari-jari, tertimbang sehingga Anda mendapatkan distribusi yang seragam:

private Point CalculatePoint()
{
    var angle = _random.NextDouble() * Math.PI * 2;
    var distance = Math.Sqrt(_random.NextDouble()) * _radius;
    var x = _originX + (distance * Math.Cos(angle));
    var y = _originY + (distance * Math.Sin(angle));
    return new Point((int)x, (int)y);
}

Sekarang Anda berpikir dengan kutub .

Anda juga dapat mempertimbangkan jarak seperti itu untuk menghindari akar kuadrat:

var distance = _random.NextDouble() + _random.NextDouble();
distance = (distance <= 1 ? distance : 2 - distance) * _radius;
Jon Purdy
sumber
Oke, jadi kami memberikan jawaban yang persis sama dalam hitungan detik perbedaan. Sekarang apa? :)
David Gouveia
@ David Gouveia Kami berdua mendapatkan upvotes karena keduanya benar. Semua orang menang! : D
Jon Purdy
Kedua jawaban itu sangat dihargai (dan tautannya juga!). Ya ampun, aku idiot karena tidak melihat itu sendiri, -1 bagiku :( Terima kasih lagi untuk kalian berdua!
DMills
Ini akan menghasilkan titik acak, tetapi tidak akan terdistribusi secara merata di atas disk, bukan? Sebaliknya mereka akan ditimbang menuju pusat. Hanya memeriksa saya tidak melewatkan sesuatu.
PeteUK
1
@ PeteUK: Anda benar, jaraknya harus ditimbang. Biarkan saya memperbarui.
Jon Purdy
3

Jika kinerja merupakan masalah, maka salah satu solusi alternatif adalah menghasilkan posisi acak dalam sebuah kotak dengan lebar / tinggi lingkaran Anda dan kemudian membuang semua titik yang tidak berada di area lingkaran.

Keuntungan dari metode ini adalah Anda tidak melakukan fungsi cos / sin / sqrt, yang tergantung pada platform Anda mungkin merupakan penghematan kecepatan yang besar.

var x = _random.NextDouble();
var y = _random.NextDouble();
if (x*x + y*y < 1.0f)
{
    // we have a usable point inside a circle
    x = x * diameter - _radius + _OriginX;
    y = y * diameter - _radius + _OriginY;
    // use the point(x,y)
}
U Jellyfish
sumber
Saya akan mencobanya dan melihat apakah itu mempercepat. Saya tidak yakin memiliki masalah kinerja, tetapi saya tetap akan mencoba ini, terima kasih!
DMills
0

Saya telah mengambil pendekatan dari salah satu komentar yang tercantum, dan memperluas fungsionalitas untuk membuat sistem pembuatan titik berbentuk donat.

            private Vector2 CalculatePosition()
            {
                double angle = _rnd.NextDouble() * Math.PI * 2;
                double radius = InnerCircleRadius + (Math.Sqrt(_rnd.NextDouble()) * (OuterCircleRadius - InnerCircleRadius));
                double x = (_context.ScreenLayout.Width * 0.5f) + (radius * Math.Cos(angle));
                double y = (_context.ScreenLayout.Height * 0.5f) + (radius * Math.Sin(angle));
                return new Vector2((int)x, (int)y);
            }

Ini adalah pendekatan yang sama seperti yang disebutkan sebelumnya tetapi memberikan hasil yang berbeda. Bagian dalam lingkaran akan dibiarkan kosong tanpa poin.

Branden
sumber